Skip to content
Snippets Groups Projects
Commit bba69ae6 authored by Eric Kooistra's avatar Eric Kooistra
Browse files

Prepare to clearly separate mm_bus combinatorial from pipelining.

parent 9516edc2
Branches
No related tags found
2 merge requests!28Master,!15Resolve L2SDP-27
-------------------------------------------------------------------------------
--
-- 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;
-------------------------------------------------------------------------------
--
-- 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;
-------------------------------------------------------------------------------
--
-- 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;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment