diff --git a/libraries/base/mm/src/vhdl/mm_bus_comb.vhd b/libraries/base/mm/src/vhdl/mm_bus_comb.vhd new file mode 100644 index 0000000000000000000000000000000000000000..8befe54e9cfb695294b7db6851dc1c6d4e1aa2d3 --- /dev/null +++ b/libraries/base/mm/src/vhdl/mm_bus_comb.vhd @@ -0,0 +1,209 @@ +------------------------------------------------------------------------------- +-- +-- Copyright 2020 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- +-- Author: E. Kooistra +-- Purpose: Connect a single MM master interface to a list of MM slave +-- interfaces using a combinatorial muliplexer as bus. +-- Description: +-- * MM bus +-- The mm_bus_comb creates a memory mapped (MM) bus that connects read +-- and write accesses from the master interface to the addressed slave +-- interface. There is one master that controls the bus and there are +-- g_nof_slaves on the bus. Per slave the start address and address span +-- have to be specified via g_base_arr and g_width_arr. +-- +-- * Slave allocation +-- The slaves have to be located on the bus such that the MSbits of the +-- global address can be used to select the slave and the LSbits of the +-- global address can directly be used to select the address within the +-- slave. Therefore: +-- . The width of a slave is the power of 2 that fits the address range of +-- the slave. +-- . The span of a slave is 2**width. +-- . The base address of a slave has to be a power of 2 multiple of the +-- slave span. +-- +-- * The mm_clk is only used when there is a slave with read latency > 0 or +-- when the MM bus uses pipelining. +-- +-- * Read latency +-- For read accesses a slave will typically have a read latency > 0, which +-- means that when the rd and address are active, then it takes read +-- latency number of clock cycles until the rddata becomes available. The +-- read latency can be specified per slave via g_rd_latency_arr. +-- The slave_index_arr is used to support that a new wr access or rd access +-- can already start, while a current rd access still has to finish with +-- a rdval. Without the slave_index_arr the master would have to wait with +-- a new rd or wr access to another slave until the read response from the +-- current slave has finished. +-- ________ +-- | delay| +-- master_mosi.address[h:w] = index --+-->| line |--\ +-- | |______| | +-- | | +-- v | +-- master_mosi --> slave_mosi_arr.wr[ ]----------------> slave_mosi_arr +-- rd | +-- v +-- master_miso <--------------------slave_miso_arr[ ]<-- slave_miso_arr +-- +-- * No pipelining +-- The mm_bus_comb is combinatorial, so there is no pipelining between +-- the master interface and the slave interfaces. +-- +-- Usage: +-- See mm_bus.vhd. +-- +-- Limitations: +-- * A limitation is that if one slave has a read latency of 2 and another +-- slave has a read latency of 1 then it is not possible to access them +-- without a gap of 1 mm_clk cycle, because the rdval will then be active +-- simultaneously from both slaves. Therefore the master can only use +-- random read access between slaves if all slaves have the same read +-- latency. For slaves that have larger read latency the master must +-- insert an gap, before it can read a slave that has less read latency. +-- An alternative workaround would be to use the same read latency for all +-- slaves on the bus, by pipelining the miso.rd, rddata for MM slaves that +-- have a smaller read latency. +-- +-- Remarks: +-- . The mm_bus_comb resembles common_mem_mux, but the difference is that +-- with common_mem_mux all slaves have the same address range and are +-- spaced without address gaps. It is possible to use common_mem_mux in +-- series with mm_bus_comb to provide hierarchy by reprensenting an array +-- of slave ports via a single slave port on the MM bus. +-- . In simulation selecting an unused element address will cause a simulation +-- failure. Therefore the element index is only accepted when it is in the +-- 0 TO g_nof_slaves-1 range. +-- +------------------------------------------------------------------------------- + + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; + +ENTITY mm_bus_comb IS + GENERIC ( + g_nof_slaves : POSITIVE; -- Number of MM slave interfaces on the bus + g_base_arr : t_nat_natural_arr; -- Address base per slave + g_width_arr : t_nat_natural_arr; -- Address width per slave + g_rd_latency_arr : t_nat_natural_arr -- Read latency per slave + ); + PORT ( + mm_clk : IN STD_LOGIC := '0'; + master_mosi : IN t_mem_mosi; + master_miso : OUT t_mem_miso; + slave_mosi_arr : OUT t_mem_mosi_arr(0 TO g_nof_slaves-1); + slave_miso_arr : IN t_mem_miso_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_miso_rst) + ); +END mm_bus_comb; + +ARCHITECTURE rtl OF mm_bus_comb IS + + -- Determine the address range of all slaves on the MM bus. + FUNCTION func_derive_mm_bus_addr_w(g_base_arr, g_width_arr : t_nat_natural_arr) RETURN NATURAL IS + VARIABLE v_base : NATURAL := 0; + VARIABLE v_width : NATURAL; + VARIABLE v_mm_bus_addr_max : NATURAL; + BEGIN + FOR I IN g_base_arr'RANGE LOOP + IF g_base_arr(I) > v_base THEN + v_base := g_base_arr(I); + v_width := g_width_arr(I); + END IF; + END LOOP; + -- Largest base address + the width of the slave at this address - 1. The + -- -1 is because the addresses count from 0 to N-1. + v_mm_bus_addr_max := v_base + 2**v_width - 1; + -- Return number of bits to represent the largest address that will be used + -- on the MM bus + RETURN ceil_log2(v_mm_bus_addr_max); + END; + + CONSTANT c_mm_bus_addr_w : NATURAL := func_derive_mm_bus_addr_w(g_base_arr, g_width_arr); + CONSTANT c_rd_latency_max : NATURAL := largest(g_rd_latency_arr); + + SIGNAL slave_index_arr : t_nat_natural_arr(0 TO c_rd_latency_max) := (OTHERS=>0); + +BEGIN + + gen_single : IF g_nof_slaves=1 GENERATE + slave_mosi_arr(0) <= master_mosi; + master_miso <= slave_miso_arr(0); + END GENERATE; + + gen_multiple : IF g_nof_slaves>1 GENERATE + -- Detect which slave in the array is addressed + p_index : PROCESS(master_mosi) + VARIABLE v_base : NATURAL; + BEGIN + slave_index_arr(0) <= g_nof_slaves; -- default index of none existing slave + FOR I IN 0 TO g_nof_slaves-1 LOOP + v_base := TO_UINT(master_mosi.address(c_mm_bus_addr_w-1 DOWNTO g_width_arr(I))); + ASSERT g_base_arr(I) MOD 2**g_width_arr(I) = 0 REPORT "Slave base address must be a multiple of the slave width." SEVERITY FAILURE; + IF v_base = g_base_arr(I) / 2**g_width_arr(I) THEN + slave_index_arr(0) <= I; -- return index of addressed slave + EXIT; + END IF; + END LOOP; + END PROCESS; + + slave_index_arr(1 TO c_rd_latency_max) <= slave_index_arr(0 TO c_rd_latency_max-1) WHEN rising_edge(mm_clk); + + -- Master access, can be write or read + p_slave_mosi_arr : PROCESS(master_mosi, slave_index_arr) + BEGIN + slave_mosi_arr <= (OTHERS=>master_mosi); -- default assign to all, to avoid latches + FOR I IN 0 TO g_nof_slaves-1 LOOP + slave_mosi_arr(I).rd <= '0'; + slave_mosi_arr(I).wr <= '0'; + IF I = slave_index_arr(0) THEN -- check index for read or write access + slave_mosi_arr(I).rd <= master_mosi.rd; + slave_mosi_arr(I).wr <= master_mosi.wr; + END IF; + END LOOP; + END PROCESS; + + + -- Slave response to read access after read latency mm_clk cycles + p_master_miso : PROCESS(slave_miso_arr, slave_index_arr) + VARIABLE v_rd_latency : NATURAL; + BEGIN + master_miso <= c_mem_miso_rst; -- default clear, to avoid latches + FOR I IN 0 TO g_nof_slaves-1 LOOP + v_rd_latency := g_rd_latency_arr(I); + IF I = slave_index_arr(v_rd_latency) THEN -- check index for read response + master_miso <= slave_miso_arr(I); + END IF; + END LOOP; + FOR I IN 0 TO g_nof_slaves-1 LOOP + IF I = slave_index_arr(0) THEN -- check index for waitrequest + master_miso.waitrequest <= slave_miso_arr(I).waitrequest; + END IF; + END LOOP; + END PROCESS; + + END GENERATE; + +END rtl; diff --git a/libraries/base/mm/src/vhdl/mm_bus_pipe.vhd b/libraries/base/mm/src/vhdl/mm_bus_pipe.vhd new file mode 100644 index 0000000000000000000000000000000000000000..8648f4b5f68b32dca86223006dcfea4fd732f847 --- /dev/null +++ b/libraries/base/mm/src/vhdl/mm_bus_pipe.vhd @@ -0,0 +1,182 @@ +------------------------------------------------------------------------------- +-- +-- Copyright 2020 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- +-- Author: E. Kooistra +-- Purpose: Provide pipelining to the combinatorial mm_bus_comb +-- Description: +-- The mm_bus_comb is combinatorial, so there is no pipelining between +-- the master interface and the slave interfaces. If possible do not +-- use pipelining of mosi and miso to avoid increasing the read latency and +-- achieve timing closure by lower clock rate for the MM bus. Pipelining the +-- MM bus can be necessary to achieve timing closure: +-- +-- * g_pipeline_mosi +-- Pipelining mosi write accesses introduces an extra latency from master +-- to slave, which is typically not a problem. Pipelining mosi read +-- accesses increases the read latency between accessing the slave and +-- getting the rddata. Using a different pipelining for the wr and the rd +-- pulse would yield a different pipelining of the address for write and +-- for read, which is akward. Therefore assume that both mosi write and +-- mosi read have the same pipelining. +-- When g_pipeline_miso_wait = TRUE then it is not necessary to use +-- g_pipeline_mosi = TRUE, because the MM latency adapter that recovers +-- the timing of the waitrequest also pipelines the mosi. +-- +-- * g_pipeline_miso_rdval +-- Pipelining the miso read data increases the read latency. +-- +-- * g_pipeline_miso_wait +-- Pipelining the miso waitrequest increases the write and read latency +-- for slaves that need MM flow control. Only applies to slave that +-- have g_waitrequest_arr is TRUE. +-- +-- The total write latency from master to slave is c_pipeline_mosi. +-- The total read latency from master via slave back to master is +-- c_pipeline_mosi + g_rd_latency_arr of the selected slave + +-- c_pipeline_miso_rdval. +-- +-- Usage: +-- See mm_bus.vhd +-- +------------------------------------------------------------------------------- + + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; + +ENTITY mm_bus_pipe IS + GENERIC ( + g_nof_slaves : POSITIVE; -- Number of MM slave interfaces on the bus + g_base_arr : t_nat_natural_arr; -- Address base per slave + g_width_arr : t_nat_natural_arr; -- Address width per slave + g_rd_latency_arr : t_nat_natural_arr; -- Read latency per slave + g_waitrequest_arr : t_nat_boolean_arr; -- Enable waitrequest flow control per slave, else fixed '0' + g_pipeline_mosi : BOOLEAN := FALSE; -- Pipeline MM access (wr, rd) + g_pipeline_miso_rdval : BOOLEAN := FALSE; -- Pipeline MM read (rdval) + g_pipeline_miso_wait : BOOLEAN := FALSE -- Pipeline MM access flow control (waitrequest) + ); + PORT ( + mm_rst : IN STD_LOGIC; + mm_clk : IN STD_LOGIC; + master_mosi : IN t_mem_mosi; + master_miso : OUT t_mem_miso; + slave_mosi_arr : OUT t_mem_mosi_arr(0 TO g_nof_slaves-1); + slave_miso_arr : IN t_mem_miso_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_miso_rst) + ); +END mm_bus_pipe; + +ARCHITECTURE str OF mm_bus_pipe IS + + SIGNAL m_mosi : t_mem_mosi := c_mem_mosi_rst; + SIGNAL m_miso : t_mem_miso := c_mem_miso_rst; + SIGNAL m_miso_reg : t_mem_miso := c_mem_miso_rst; + + SIGNAL bus_mosi_arr : t_mem_mosi_arr(0 TO g_nof_slaves-1); + SIGNAL bus_miso_arr : t_mem_miso_arr(0 TO g_nof_slaves-1); + SIGNAL pipe_mosi_arr : t_mem_mosi_arr(0 TO g_nof_slaves-1); + SIGNAL pipe_miso_arr : t_mem_miso_arr(0 TO g_nof_slaves-1); + SIGNAL adapt_mosi_arr : t_mem_mosi_arr(0 TO g_nof_slaves-1); + SIGNAL adapt_miso_arr : t_mem_miso_arr(0 TO g_nof_slaves-1); + +BEGIN + + -- Master side + m_mosi <= master_mosi; + + m_miso_reg <= m_miso WHEN rising_edge(mm_clk); + + p_miso_pipe : PROCESS(m_miso, m_miso_reg) + BEGIN + -- Default no miso pipelining + master_miso <= m_miso; + -- Use pipelining + IF g_pipeline_miso_rdval THEN + master_miso.rddata <= m_miso_reg.rddata; + master_miso.rdval <= m_miso_reg.rdval; + END IF; + IF g_pipeline_miso_wait THEN + master_miso.waitrequest <= m_miso_reg.waitrequest; + END IF; + END PROCESS; + + -- MM bus + u_mm_bus_comb : ENTITY work.mm_bus_comb + GENERIC MAP ( + g_nof_slaves => g_nof_slaves, + g_base_arr => g_base_arr, + g_width_arr => g_width_arr, + g_rd_latency_arr => g_rd_latency_arr + ) + PORT MAP ( + mm_clk => mm_clk, + master_mosi => m_mosi, + master_miso => m_miso, + slave_mosi_arr => bus_mosi_arr, + slave_miso_arr => bus_miso_arr + ); + + -- Slaves side + gen_slave_pipes : FOR I IN 0 TO g_nof_slaves-1 GENERATE + u_slave_pipe_mosi : ENTITY work.mm_pipeline + GENERIC MAP ( + g_pipeline => g_pipeline_mosi + ) + PORT MAP ( + mm_rst => mm_rst, + mm_clk => mm_clk, + -- MM input RL = 1 + in_mosi => bus_mosi_arr(I), + in_miso => bus_miso_arr(I), + -- MM output RL = 0 + out_mosi => pipe_mosi_arr(I), + out_miso => pipe_miso_arr(I) + ); + + gen_wires : IF g_waitrequest_arr(I) = FALSE GENERATE + adapt_mosi_arr(I) <= pipe_mosi_arr(I); + pipe_miso_arr(I) <= adapt_miso_arr(I); + END GENERATE; + + gen_slave_latency_adapter : IF g_waitrequest_arr(I) = TRUE GENERATE + u_slave_latency_adapter : ENTITY work.mm_latency_adapter + GENERIC MAP ( + g_adapt => g_pipeline_miso_wait + ) + PORT MAP ( + mm_rst => mm_rst, + mm_clk => mm_clk, + -- MM input RL = 1 + in_mosi => pipe_mosi_arr(I), + in_miso => pipe_miso_arr(I), + -- MM output RL = 0 + out_mosi => adapt_mosi_arr(I), + out_miso => adapt_miso_arr(I) + ); + END GENERATE; + END GENERATE; + + slave_mosi_arr <= adapt_mosi_arr; + adapt_miso_arr <= slave_miso_arr; + +END str; diff --git a/libraries/base/mm/src/vhdl/mm_pipeline.vhd b/libraries/base/mm/src/vhdl/mm_pipeline.vhd new file mode 100644 index 0000000000000000000000000000000000000000..09f3cda1a1c471c9d535d561bf7338b9c2a25111 --- /dev/null +++ b/libraries/base/mm/src/vhdl/mm_pipeline.vhd @@ -0,0 +1,82 @@ +------------------------------------------------------------------------------- +-- +-- Copyright 2020 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- +-- Author: E. Kooistra +-- Purpose: Pipeline MM mosi +-- Description: +-- The mm_pipeline mosi registers the in_mosi if g_pipeline = TRUE, else it +-- defaults to wires. +-- Remark: +-- * The mm_pipeline could be optimized regarding the miso.waitrequest flow +-- control if it would be implemented similar as dp_pipeline.vhd. This +-- involves using the pipeline register to accept an access when it is +-- empty. In this way the waitrequest to the in_mosi only needs to apply +-- when the out_miso is not ready and the pipeline is full. The advantage +-- of simply registering in_mosi and wiring in_miso is that it is simpler +-- and does not put extra logic into the combinatorial miso.waitrequest +-- path. + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; + +ENTITY mm_pipeline IS + GENERIC ( + g_pipeline : BOOLEAN := TRUE + ); + PORT ( + mm_rst : IN STD_LOGIC; + mm_clk : IN STD_LOGIC; + -- MM input RL = 1 + in_mosi : IN t_mem_mosi; + in_miso : OUT t_mem_miso; + -- MM output RL = 0 + out_mosi : OUT t_mem_mosi; + out_miso : IN t_mem_miso + ); +END mm_pipeline; + + +ARCHITECTURE rtl OF mm_pipeline IS + + SIGNAL in_mosi_reg : t_mem_mosi := c_mem_mosi_rst; + +BEGIN + + -- Pass on miso + in_miso <= out_miso; + + -- Optionally pipeline the mosi + --in_mosi_reg <= in_mosi WHEN rising_edge(mm_clk); -- without mm_rst + p_reg : PROCESS(mm_rst, mm_clk) -- with mm_rst + BEGIN + IF mm_rst = '1' THEN + in_mosi_reg <= c_mem_mosi_rst; + ELSIF rising_edge(mm_clk) THEN + in_mosi_reg <= in_mosi; + END IF; + END PROCESS; + + out_mosi <= in_mosi_reg WHEN g_pipeline = TRUE ELSE in_mosi; + +END rtl;