diff --git a/libraries/base/mm/src/vhdl/mm_bus_comb.vhd b/libraries/base/mm/src/vhdl/mm_bus_comb.vhd index 77302f2c02d85e625e583efd7d4396acf9145ebc..20bcd6b326d73c34ae03b14f16c075e025f16d30 100644 --- a/libraries/base/mm/src/vhdl/mm_bus_comb.vhd +++ b/libraries/base/mm/src/vhdl/mm_bus_comb.vhd @@ -1,214 +1,214 @@ -------------------------------------------------------------------------------- --- --- 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, to --- pipeline the slave_index_arr for the master_miso.rddata/rdval. --- Typically a master will wait for the last rdval, before accessing --- another slave port, so then it is not benecessary to pipeline the --- slave_index_arr. However registering the slave_index_arr eases timing --- closure on the miso part and will allow reading from different slave --- ports without waiting, provided that both slaves have the same read --- latency. --- --- * 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. Use mm_bus_pipe to add --- pipelining. --- --- 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. --- -------------------------------------------------------------------------------- - - -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; +------------------------------------------------------------------------------- +-- +-- 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, to +-- pipeline the slave_index_arr for the master_miso.rddata/rdval. +-- Typically a master will wait for the last rdval, before accessing +-- another slave port, so then it is not benecessary to pipeline the +-- slave_index_arr. However registering the slave_index_arr eases timing +-- closure on the miso part and will allow reading from different slave +-- ports without waiting, provided that both slaves have the same read +-- latency. +-- +-- * 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. Use mm_bus_pipe to add +-- pipelining. +-- +-- 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. +-- +------------------------------------------------------------------------------- + + +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_master_mux.vhd b/libraries/base/mm/src/vhdl/mm_master_mux.vhd index ccfa875c277f65c4395775ed53ffe8f403e23f2d..abac065ce676b536b61ea87e0d741c85253c3bd0 100644 --- a/libraries/base/mm/src/vhdl/mm_master_mux.vhd +++ b/libraries/base/mm/src/vhdl/mm_master_mux.vhd @@ -107,7 +107,10 @@ BEGIN FOR I IN 0 TO g_nof_masters-1 LOOP IF master_mosi_arr(I).wr='1' OR master_mosi_arr(I).rd='1' THEN index <= I; -- index of active master - EXIT; + EXIT; -- Found active master, no need to loop further. EXIT is not + -- realy needed, because there should be only one active + -- master, and if there are more active masters, then it + -- does not matter whether the first or the last is selected. END IF; END LOOP; END PROCESS;