Add VHDL for DMA write channel

This commit is contained in:
Joris van Rantwijk 2024-08-09 20:16:53 +02:00
parent f58343fc0f
commit 5632ffc6b2
7 changed files with 583 additions and 122 deletions

View File

@ -207,7 +207,8 @@ architecture arch of dma_axi_master is
limit: std_logic_vector(31 downto 12))
return boolean
is begin
return (unsigned(limit) /= 0) and (unsigned(addr) <= shift_left(unsigned(limit), 9) - transfer_size);
return (unsigned(limit) /= 0)
and (unsigned(addr) <= shift_left(resize(unsigned(limit), 29), 9) - transfer_size);
end function;
-- Calculate tha AXI address for a DMA transfer by adding the address offset from the client

View File

@ -0,0 +1,250 @@
--
-- Management of DMA transfers from FPGA to memory.
--
-- The AXI master MUST be configured for 16 beats per transfer.
--
-- Joris van Rantwijk 2024
--
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.puzzlefw_pkg.all;
entity dma_write_channel is
generic (
-- Size of the input data queue as 2-log of the number of words.
queue_size_bits: integer range 5 to 16 := 10
);
port (
-- Main clock, active on rising edge.
clk: in std_logic;
-- Reset, active high, synchronous to main clock.
reset: in std_logic;
-- High to enable the channel, low to pause the channel.
-- When channel_en is low, any ongoing transfer will be completed but
-- no new transfer will be started.
channel_en: in std_logic;
-- High to initialize the channel.
--
-- This resets the write pointer to the start of the buffer and deletes
-- all data from the queue.
--
-- This function must be used whenever the buffer address range is changed.
-- This function must not be used while uncompleted DMA transfers
-- are in progress.
channel_init: in std_logic;
-- Start and end address of the DMA buffer.
-- Both addresses are relative to the DMA window base address.
-- The write pointer wraps to the start address when it would
-- have reached the end address.
addr_start: in std_logic_vector(31 downto 7);
addr_end: in std_logic_vector(31 downto 7);
-- When the write pointer reaches the limit address, DMA transfers
-- are paused until software updates the limit.
addr_limit: in std_logic_vector(31 downto 7);
-- An interrupt is triggered when the write pointer equals the
-- interrupt address.
addr_interrupt: in std_logic_vector(31 downto 7);
-- Write pointer.
addr_pointer: out std_logic_vector(31 downto 7);
-- High to enable interrupt on reaching a configured address.
intr_en: in std_logic;
-- Pulsed high to clear a previous interrupt condition.
intr_clear: in std_logic;
-- High if an enabled interrupt condition occurred and the interrupt
-- has not been cleared yet.
intr_out: out std_logic;
-- Input data stream to the channel.
in_valid: in std_logic;
in_ready: out std_logic;
in_data: in dma_data_type;
-- Signals to AXI master.
write_cmd_addr: out dma_address_type;
write_cmd_valid: out std_logic;
write_cmd_ready: in std_logic;
write_data: out dma_data_type;
write_data_ready: in std_logic;
write_finished: in std_logic
);
end entity;
architecture arch of dma_write_channel is
-- Number of beats per DMA transfer.
-- The AXI master must use the same transfer size.
-- Address alignments must match the transfer size.
constant transfer_size: integer := 16;
type state_type is (STATE_IDLE, STATE_START, STATE_FLOW);
type regs_type is record
cmd_valid: std_logic;
cmd_addr: std_logic_vector(31 downto 7);
addr_pointer: std_logic_vector(31 downto 7);
intr_out: std_logic;
state: state_type;
pending_beats: unsigned(4 downto 0);
end record;
constant regs_init: regs_type := (
cmd_valid => '0',
cmd_addr => (others => '0'),
addr_pointer => (others => '0'),
intr_out => '0',
state => STATE_IDLE,
pending_beats => (others => '0')
);
signal r: regs_type := regs_init;
signal rnext: regs_type;
signal s_fifo_reset: std_logic;
signal s_fifo_length: unsigned(queue_size_bits - 1 downto 0);
begin
--
-- Data FIFO.
--
inst_fifo: entity work.simple_fifo
generic map (
data_width => 64,
fifo_depth_bits => queue_size_bits )
port map (
clk => clk,
reset => s_fifo_reset,
in_valid => in_valid,
in_ready => in_ready,
in_data => in_data,
out_valid => open,
out_ready => write_data_ready,
out_data => write_data,
queue_length => s_fifo_length );
-- Drive output ports.
addr_pointer <= r.addr_pointer;
intr_out <= r.intr_out;
write_cmd_addr <= r.cmd_addr & "0000";
write_cmd_valid <= r.cmd_valid;
-- Drive reset signal to FIFO.
s_fifo_reset <= reset or channel_init;
--
-- Combinatorial process.
--
process (all) is
variable v: regs_type;
begin
-- Load current register values.
v := r;
-- State machine.
case r.state is
when STATE_IDLE =>
-- Issue a new transfer when possible.
-- Data for the whole transfer must be ready in the FIFO.
if (channel_en = '1')
and (s_fifo_length >= transfer_size)
and (r.cmd_addr /= addr_limit) then
v.cmd_valid := '1';
v.state := STATE_START;
v.pending_beats := to_unsigned(transfer_size, v.pending_beats'length);
end if;
when STATE_START =>
-- Wait until start of transfer.
if write_cmd_ready = '1' then
v.cmd_valid := '0';
-- Update command address.
v.cmd_addr := std_logic_vector(unsigned(r.cmd_addr) + 1);
if v.cmd_addr = addr_end then
-- Wrap at end of buffer.
v.cmd_addr := addr_start;
end if;
v.state := STATE_FLOW;
end if;
when STATE_FLOW =>
-- Wait until last beat.
if (write_data_ready = '1') and (r.pending_beats = 1) then
v.state := STATE_IDLE;
end if;
end case;
-- Count write beats to find end of transfer.
if write_data_ready = '1' then
v.pending_beats := r.pending_beats - 1;
end if;
-- Update pointer on write completion.
if write_finished = '1' then
v.addr_pointer := std_logic_vector(unsigned(r.addr_pointer) + 1);
if v.addr_pointer = addr_end then
-- Wrap at end of buffer.
v.addr_pointer := addr_start;
end if;
end if;
-- Clear interrupt.
if intr_clear = '1' then
v.intr_out := '0';
end if;
-- Raise interrupt when pointer equals threshold.
if (intr_en = '1') and (r.addr_pointer = addr_interrupt) then
v.intr_out := '1';
end if;
-- Initialize channel.
if channel_init = '1' then
v.cmd_valid := '0';
v.cmd_addr := addr_start;
v.addr_pointer := addr_start;
v.intr_out := '0';
v.state := STATE_IDLE;
end if;
-- Synchronous reset.
if reset = '1' then
v := regs_init;
end if;
-- Drive new register values to synchronous process.
rnext <= v;
end process;
--
-- Synchronous process.
--
process (clk) is
begin
if rising_edge(clk) then
r <= rnext;
end if;
end process;
end architecture;

View File

@ -20,25 +20,29 @@ package puzzlefw_pkg is
type dma_data_array is array(natural range <>) of dma_data_type;
-- Register addresses.
constant reg_addr_mask: std_logic_vector(31 downto 0) := x"0010fffc";
constant reg_info: natural := 16#000000#;
constant reg_irq_enable: natural := 16#000010#;
constant reg_dma_en: natural := 16#000100#;
constant reg_dma_status: natural := 16#000104#;
constant reg_dma_clear: natural := 16#000108#;
constant reg_rcnt: natural := 16#000200#;
constant reg_wcnt: natural := 16#000204#;
constant reg_start: natural := 16#000208#;
constant reg_test_irq: natural := 16#000400#;
constant reg_test_led: natural := 16#000404#;
constant reg_dma_buf_addr: natural := 16#100000#;
constant reg_dma_buf_size: natural := 16#100004#;
constant reg_addr_mask: std_logic_vector(31 downto 0) := x"0010fffc";
constant reg_info: natural := 16#000000#;
constant reg_irq_enable: natural := 16#000010#;
constant reg_irq_pending: natural := 16#000014#;
constant reg_dma_en: natural := 16#000100#;
constant reg_dma_status: natural := 16#000104#;
constant reg_dma_clear: natural := 16#000108#;
constant reg_acq_addr_start: natural := 16#000200#;
constant reg_acq_addr_end: natural := 16#000204#;
constant reg_acq_addr_limit: natural := 16#000208#;
constant reg_acq_addr_intr: natural := 16#00020c#;
constant reg_acq_addr_ptr: natural := 16#000210#;
constant reg_acq_channel_ctrl: natural := 16#000214#;
constant reg_acq_intr_ctrl: natural := 16#000218#;
constant reg_test_led: natural := 16#000404#;
constant reg_dma_buf_addr: natural := 16#100000#;
constant reg_dma_buf_size: natural := 16#100004#;
-- Firmware info word.
constant fw_api_version: natural := 1;
constant fw_version_major: natural := 0;
constant fw_version_minor: natural := 2;
constant fw_info_word: std_logic_vector(31 downto 0) :=
constant fw_api_version: natural := 1;
constant fw_version_major: natural := 0;
constant fw_version_minor: natural := 3;
constant fw_info_word: std_logic_vector(31 downto 0) :=
x"4a"
& std_logic_vector(to_unsigned(fw_api_version, 8))
& std_logic_vector(to_unsigned(fw_version_major, 8))
@ -50,42 +54,54 @@ package puzzlefw_pkg is
-- Control registers: read/write access by processor, output signals to FPGA.
type registers_control is record
irq_enable: std_logic;
test_irq: std_logic_vector(7 downto 0);
test_led: std_logic_vector(7 downto 0);
dma_en: std_logic;
acq_addr_start: std_logic_vector(31 downto 7);
acq_addr_end: std_logic_vector(31 downto 7);
acq_addr_limit: std_logic_vector(31 downto 7);
acq_addr_intr: std_logic_vector(31 downto 7);
acq_channel_en: std_logic;
acq_intr_en: std_logic;
dma_buf_addr: std_logic_vector(31 downto 12);
dma_buf_size: std_logic_vector(31 downto 12);
end record;
-- Status registers: input signals from FPGA, read-only access by processor.
type registers_status is record
irq_pending: std_logic_vector(0 downto 0);
dma_busy: std_logic;
dma_err_read: std_logic;
dma_err_write: std_logic;
dma_err_address: std_logic;
dma_err_any: std_logic;
rcnt: unsigned(31 downto 0);
wcnt: unsigned(31 downto 0);
acq_addr_ptr: std_logic_vector(31 downto 7);
end record;
-- Trigger registers: write-only access by processor, single-cycle pulse signals to FPGA.
type registers_trigger is record
dma_clear: std_logic;
start: std_logic;
acq_channel_init: std_logic;
acq_intr_clear: std_logic;
end record;
constant registers_control_init: registers_control := (
irq_enable => '0',
test_irq => (others => '0'),
test_led => (others => '0'),
dma_en => '0',
acq_addr_start => (others => '0'),
acq_addr_end => (others => '0'),
acq_addr_limit => (others => '0'),
acq_addr_intr => (others => '0'),
acq_channel_en => '0',
acq_intr_en => '0',
dma_buf_addr => (others => '0'),
dma_buf_size => (others => '0')
);
constant registers_trigger_init: registers_trigger := (
dma_clear => '0',
start => '0'
acq_channel_init => '0',
acq_intr_clear => '0'
);
end package;

View File

@ -6,6 +6,7 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_misc.all;
use ieee.numeric_std.all;
library unisim;
@ -127,23 +128,24 @@ architecture arch of puzzlefw_top is
signal s_axi_rvalid: std_logic;
signal s_axi_rready: std_logic;
signal s_irq: std_logic_vector(7 downto 0);
signal s_dma_interrupt: std_logic;
signal s_irq_pending: std_logic_vector(0 downto 0);
signal s_irq_f2p: std_logic_vector(7 downto 0);
signal s_reg_control: registers_control;
signal s_reg_status: registers_status;
signal s_reg_trigger: registers_trigger;
signal s_dma_read_cmd_addr: dma_address_type;
signal s_dma_read_cmd_valid: std_logic;
signal s_dma_read_cmd_ready: std_logic;
signal s_dma_read_data: dma_data_type;
signal s_dma_read_data_valid: std_logic;
signal s_dma_write_cmd_addr: dma_address_type;
signal s_dma_write_cmd_valid: std_logic;
signal s_dma_write_cmd_ready: std_logic;
signal s_dma_write_data: dma_data_type;
signal s_dma_write_data_ready: std_logic;
signal s_dma_write_finished: std_logic;
signal s_dma_write_cmd_addr: dma_address_array(0 downto 0);
signal s_dma_write_cmd_valid: std_logic_vector(0 downto 0);
signal s_dma_write_cmd_ready: std_logic_vector(0 downto 0);
signal s_dma_write_data: dma_data_array(0 downto 0);
signal s_dma_write_data_ready: std_logic_vector(0 downto 0);
signal s_dma_write_finished: std_logic_vector(0 downto 0);
signal s_dma_in_valid: std_logic;
signal s_dma_in_ready: std_logic;
signal s_dma_in_data: std_logic_vector(63 downto 0);
signal r_test_prefetch: std_logic;
signal r_test_raddr: std_logic_vector(9 downto 0);
@ -153,8 +155,6 @@ architecture arch of puzzlefw_top is
begin
s_irq(7 downto 1) <= s_reg_control.test_irq(7 downto 1);
s_irq(0) <= s_reg_control.test_irq(0) and s_reg_control.irq_enable;
led_o(7 downto 2) <= s_reg_control.test_led(7 downto 2);
-- Differential clock input for ADC clock.
@ -219,7 +219,7 @@ begin
APB_M_0_pslverr(0) => s_apb_pslverr,
APB_M_0_pwdata => s_apb_pwdata,
APB_M_0_pwrite => s_apb_pwrite,
IRQ_F2P => s_irq,
IRQ_F2P => s_irq_f2p,
S_AXI_HP0_0_araddr => s_axi_araddr,
S_AXI_HP0_0_arburst => s_axi_arburst,
S_AXI_HP0_0_arcache => s_axi_arcache,
@ -282,7 +282,7 @@ begin
inst_axi_master: entity work.dma_axi_master
generic map (
transfer_size => 16,
num_read_channels => 1,
num_read_channels => 0,
num_write_channels => 1 )
port map (
clk => clk_adc,
@ -296,17 +296,17 @@ begin
err_address => s_reg_status.dma_err_address,
err_any => s_reg_status.dma_err_any,
clear_errors => s_reg_trigger.dma_clear,
read_cmd_addr(0) => s_dma_read_cmd_addr,
read_cmd_valid(0) => s_dma_read_cmd_valid,
read_cmd_ready(0) => s_dma_read_cmd_ready,
read_data(0) => s_dma_read_data,
read_data_valid(0) => s_dma_read_data_valid,
write_cmd_addr(0) => s_dma_write_cmd_addr,
write_cmd_valid(0) => s_dma_write_cmd_valid,
write_cmd_ready(0) => s_dma_write_cmd_ready,
write_data(0) => s_dma_write_data,
write_data_ready(0) => s_dma_write_data_ready,
write_finished(0) => s_dma_write_finished,
read_cmd_addr => (others => (others => '0')),
read_cmd_valid => (others => '0'),
read_cmd_ready => open,
read_data => open,
read_data_valid => open,
write_cmd_addr => s_dma_write_cmd_addr,
write_cmd_valid => s_dma_write_cmd_valid,
write_cmd_ready => s_dma_write_cmd_ready,
write_data => s_dma_write_data,
write_data_ready => s_dma_write_data_ready,
write_finished => s_dma_write_finished,
m_axi_awid => s_axi_awid,
m_axi_awaddr => s_axi_awaddr,
m_axi_awlen => s_axi_awlen,
@ -347,76 +347,47 @@ begin
m_axi_rready => s_axi_rready
);
-- little test machine for DMA
inst_mem: xpm_memory_sdpram
-- DMA Write Channel
inst_write_channel: entity work.dma_write_channel
generic map (
CLOCKING_MODE => "common_clock",
MEMORY_PRIMITIVE => "block",
MEMORY_SIZE => 1024 * 64,
ADDR_WIDTH_A => 10,
ADDR_WIDTH_B => 10,
WRITE_DATA_WIDTH_A => 64,
BYTE_WRITE_WIDTH_A => 64,
READ_DATA_WIDTH_B => 64,
READ_LATENCY_B => 1 )
queue_size_bits => 10 )
port map (
clka => clk_adc,
clkb => clk_adc,
rstb => s_reset,
addra => r_test_waddr,
addrb => r_test_raddr,
dina => s_dma_read_data,
doutb => s_test_dout,
ena => s_dma_read_data_valid,
enb => s_test_ren,
wea => "1",
regceb => '1',
injectdbiterra => '0',
injectsbiterra => '0',
sleep => '0' );
clk => clk_adc,
reset => s_reset,
channel_en => s_reg_control.acq_channel_en,
channel_init => s_reg_trigger.acq_channel_init,
addr_start => s_reg_control.acq_addr_start,
addr_end => s_reg_control.acq_addr_end,
addr_limit => s_reg_control.acq_addr_limit,
addr_interrupt => s_reg_control.acq_addr_intr,
addr_pointer => s_reg_status.acq_addr_ptr,
intr_en => s_reg_control.acq_intr_en,
intr_clear => s_reg_trigger.acq_intr_clear,
intr_out => s_dma_interrupt,
in_valid => s_dma_in_valid,
in_ready => s_dma_in_ready,
in_data => s_dma_in_data,
write_cmd_addr => s_dma_write_cmd_addr(0),
write_cmd_valid => s_dma_write_cmd_valid(0),
write_cmd_ready => s_dma_write_cmd_ready(0),
write_data => s_dma_write_data(0),
write_data_ready => s_dma_write_data_ready(0),
write_finished => s_dma_write_finished(0) );
s_dma_write_data <= not s_test_dout;
s_test_ren <= r_test_prefetch or s_dma_write_data_ready;
-- Collect interrupt signals from peripherals and generate interrupt to PS.
s_irq_pending(0) <= s_dma_interrupt;
s_reg_status.irq_pending <= s_irq_pending;
s_irq_f2p(0) <= s_reg_control.irq_enable and or_reduce(s_irq_pending);
s_irq_f2p(7 downto 1) <= (others => '0');
-- little test machine for DMA
process (clk_adc) is
begin
if rising_edge(clk_adc) then
r_test_prefetch <= '0';
if s_dma_read_cmd_ready = '1' and s_dma_read_cmd_valid = '1' then
if unsigned(s_dma_read_cmd_addr) < 1008 then
s_dma_read_cmd_addr <= std_logic_vector(unsigned(s_dma_read_cmd_addr) + 16);
else
s_dma_read_cmd_valid <= '0';
end if;
end if;
if s_dma_read_data_valid = '1' then
s_reg_status.rcnt <= s_reg_status.rcnt + 1;
r_test_waddr <= std_logic_vector(unsigned(r_test_waddr) + 1);
if unsigned(r_test_waddr) = 1023 then
s_dma_write_cmd_valid <= '1';
r_test_prefetch <= '1';
end if;
end if;
if s_dma_write_cmd_ready = '1' and s_dma_write_cmd_valid = '1' then
if unsigned(s_dma_write_cmd_addr) < 1008 then
s_dma_write_cmd_addr <= std_logic_vector(unsigned(s_dma_write_cmd_addr) + 16);
else
s_dma_write_cmd_valid <= '0';
end if;
end if;
if (r_test_prefetch = '1') or (s_dma_write_data_ready = '1') then
r_test_raddr <= std_logic_vector(unsigned(r_test_raddr) + 1);
end if;
if s_dma_write_finished = '1' then
s_reg_status.wcnt <= s_reg_status.wcnt + 1;
end if;
if s_reg_trigger.start = '1' then
r_test_waddr <= (others => '0');
r_test_raddr <= (others => '0');
s_dma_read_cmd_addr <= (others => '0');
s_dma_write_cmd_addr <= (others => '0');
s_dma_read_cmd_valid <= '1';
s_dma_write_cmd_valid <= '0';
s_dma_in_valid <= '1';
if s_dma_in_ready = '1' then
s_dma_in_data(15 downto 0) <= std_logic_vector(unsigned(s_dma_in_data(15 downto 0)) + 1);
s_dma_in_data(63 downto 16) <= std_logic_vector(unsigned(s_dma_in_data(63 downto 16)) - 1);
end if;
end if;
end process;

View File

@ -90,6 +90,7 @@ begin
case to_integer(unsigned(apb_paddr and reg_addr_mask)) is
when reg_info => v.prdata := fw_info_word;
when reg_irq_enable => v.prdata(0) := r.reg_control.irq_enable;
when reg_irq_pending => v.prdata(0 downto 0) := reg_status.irq_pending;
when reg_dma_en => v.prdata(0) := r.reg_control.dma_en;
when reg_dma_status =>
v.prdata(0) := reg_status.dma_busy;
@ -97,12 +98,16 @@ begin
v.prdata(2) := reg_status.dma_err_write;
v.prdata(3) := reg_status.dma_err_address;
v.prdata(4) := reg_status.dma_err_any;
when reg_rcnt => v.prdata := std_logic_vector(reg_status.rcnt);
when reg_wcnt => v.prdata := std_logic_vector(reg_status.wcnt);
when reg_acq_addr_start => v.prdata(31 downto 7) := r.reg_control.acq_addr_start;
when reg_acq_addr_end => v.prdata(31 downto 7) := r.reg_control.acq_addr_end;
when reg_acq_addr_limit => v.prdata(31 downto 7) := r.reg_control.acq_addr_limit;
when reg_acq_addr_intr => v.prdata(31 downto 7) := r.reg_control.acq_addr_intr;
when reg_acq_addr_ptr => v.prdata(31 downto 7) := reg_status.acq_addr_ptr;
when reg_acq_channel_ctrl => v.prdata(0) := r.reg_control.acq_channel_en;
when reg_acq_intr_ctrl => v.prdata(0) := r.reg_control.acq_intr_en;
when reg_test_led => v.prdata(7 downto 0) := r.reg_control.test_led;
when reg_dma_buf_addr => v.prdata(31 downto 12) := r.reg_control.dma_buf_addr;
when reg_dma_buf_size => v.prdata(31 downto 12) := r.reg_control.dma_buf_size;
when reg_test_irq => v.prdata(7 downto 0) := r.reg_control.test_irq;
when reg_test_led => v.prdata(7 downto 0) := r.reg_control.test_led;
when others => null;
end case;
@ -114,15 +119,23 @@ begin
when reg_irq_enable => v.reg_control.irq_enable := apb_pwdata(0);
when reg_dma_en => v.reg_control.dma_en := apb_pwdata(0);
when reg_dma_clear => v.reg_trigger.dma_clear := apb_pwdata(0);
when reg_start => v.reg_trigger.start := apb_pwdata(0);
when reg_acq_addr_start => v.reg_control.acq_addr_start := apb_pwdata(31 downto 7);
when reg_acq_addr_end => v.reg_control.acq_addr_end := apb_pwdata(31 downto 7);
when reg_acq_addr_limit => v.reg_control.acq_addr_limit := apb_pwdata(31 downto 7);
when reg_acq_addr_intr => v.reg_control.acq_addr_intr := apb_pwdata(31 downto 7);
when reg_acq_channel_ctrl =>
v.reg_control.acq_channel_en := apb_pwdata(0);
v.reg_trigger.acq_channel_init := apb_pwdata(1);
when reg_acq_intr_ctrl =>
v.reg_control.acq_intr_en := apb_pwdata(0);
v.reg_trigger.acq_intr_clear := apb_pwdata(1);
when reg_test_led => v.reg_control.test_led := apb_pwdata(7 downto 0);
when reg_dma_buf_addr => v.reg_control.dma_buf_addr := apb_pwdata(31 downto 12);
when reg_dma_buf_size => v.reg_control.dma_buf_size := apb_pwdata(31 downto 12);
when reg_test_irq => v.reg_control.test_irq := apb_pwdata(7 downto 0);
when reg_test_led => v.reg_control.test_led := apb_pwdata(7 downto 0);
when others => null;
end case;
end if;
-- Synchronous reset.
if reset = '1' then
v := regs_init;

208
fpga/rtl/simple_fifo.vhd Normal file
View File

@ -0,0 +1,208 @@
--
-- Simple FIFO with single clock.
--
-- Joris van Rantwijk 2024
--
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library xpm;
use xpm.vcomponents.all;
use work.puzzlefw_pkg.all;
entity simple_fifo is
generic (
-- Word width.
data_width: integer range 1 to 1024;
-- Size of FIFO as 2-log of the number of words.
fifo_depth_bits: integer range 2 to 16
);
port (
-- Main clock, active on rising edge.
clk: in std_logic;
-- Reset, active high, synchronous to main clock.
-- After reset, the FIFO is empty.
reset: in std_logic;
-- Data input stream.
in_valid: in std_logic;
in_ready: out std_logic;
in_data: in std_logic_vector(data_width - 1 downto 0);
-- Data output stream.
out_valid: out std_logic;
out_ready: in std_logic;
out_data: out std_logic_vector(data_width - 1 downto 0);
-- Number of words currently in the FIFO.
-- This excludes the word currently expressed on "out_ready" when "out_valid" is high.
queue_length: out unsigned(fifo_depth_bits - 1 downto 0)
);
end entity;
architecture arch of simple_fifo is
type regs_type is record
in_ready: std_logic;
out_valid: std_logic;
nonempty: std_logic;
waddr: unsigned(fifo_depth_bits - 1 downto 0);
raddr: unsigned(fifo_depth_bits - 1 downto 0);
qlen: unsigned(fifo_depth_bits - 1 downto 0);
end record;
constant regs_init: regs_type := (
in_ready => '0',
out_valid => '0',
nonempty => '0',
waddr => (others => '0'),
raddr => (others => '0'),
qlen => (others => '0')
);
signal r: regs_type := regs_init;
signal rnext: regs_type;
signal s_ram_wen: std_logic;
signal s_ram_ren: std_logic;
begin
--
-- Xilinx Simple Dual Port RAM.
--
ram_inst: xpm_memory_sdpram
generic map (
ADDR_WIDTH_A => fifo_depth_bits,
ADDR_WIDTH_B => fifo_depth_bits,
AUTO_SLEEP_TIME => 0,
BYTE_WRITE_WIDTH_A => data_width,
CASCADE_HEIGHT => 0,
CLOCKING_MODE => "common_clock",
ECC_MODE => "no_ecc",
MEMORY_INIT_FILE => "none",
MEMORY_INIT_PARAM => "0",
MEMORY_OPTIMIZATION => "true",
MEMORY_PRIMITIVE => "block",
MEMORY_SIZE => data_width * 2**fifo_depth_bits,
MESSAGE_CONTROL => 0,
READ_DATA_WIDTH_B => data_width,
READ_LATENCY_B => 1,
READ_RESET_VALUE_B => "0",
RST_MODE_A => "SYNC",
RST_MODE_B => "SYNC",
SIM_ASSERT_CHK => 0,
USE_EMBEDDED_CONSTRAINT => 0,
USE_MEM_INIT => 0,
WAKEUP_TIME => "disable_sleep",
WRITE_DATA_WIDTH_A => data_width,
WRITE_MODE_B => "no_change" )
port map (
addra => std_logic_vector(r.waddr),
addrb => std_logic_vector(r.raddr),
clka => clk,
clkb => clk,
dbiterrb => open,
dina => in_data,
doutb => out_data,
ena => s_ram_wen,
enb => s_ram_ren,
injectdbiterra => '0',
injectsbiterra => '0',
regceb => '1',
rstb => reset,
sbiterrb => open,
sleep => '0',
wea => "1" );
--
-- Drive output ports.
--
in_ready <= r.in_ready;
out_valid <= r.out_valid;
queue_length <= r.qlen;
--
-- Drive control signals to RAM.
--
s_ram_wen <= in_valid and r.in_ready;
s_ram_ren <= r.nonempty and (out_ready or (not r.out_valid));
--
-- Combinatorial process.
--
process (all) is
variable v: regs_type;
begin
-- Load current register values.
v := r;
-- Update write pointer on write.
if (in_valid = '1') and (r.in_ready = '1') then
v.waddr := r.waddr + 1;
end if;
-- Update input ready status.
-- Note this uses the NEW value of the write pointer.
if v.waddr + 1 = r.raddr then
v.in_ready := '0';
else
v.in_ready := '1';
end if;
-- On read from RAM, update read pointer.
if (r.nonempty = '1') and (out_ready = '1' or r.out_valid = '0') then
v.raddr := r.raddr + 1;
end if;
-- Determine whether output word is valid in the next cycle.
if r.nonempty = '1' then
-- Keep output word or load a new word during this cycle.
v.out_valid := '1';
elsif out_ready = '1' then
-- Consume output word without refreshing it.
v.out_valid := '0';
end if;
-- Determine whether RAM will contain data on the next cycle.
-- Note this uses the NEW values of the read and write pointers.
if v.waddr = v.raddr then
v.nonempty := '0';
else
v.nonempty := '1';
end if;
-- Calculate queue length.
-- Note this uses the NEW values of "waddr" and "raddr".
v.qlen := v.waddr - v.raddr;
-- Synchronous reset.
if reset = '1' then
v := regs_init;
end if;
-- Drive new register values to synchronous process.
rnext <= v;
end process;
--
-- Synchronous process.
--
process (clk) is
begin
if rising_edge(clk) then
r <= rnext;
end if;
end process;
end architecture;

View File

@ -19,7 +19,9 @@ set_property target_language VHDL [current_project]
# Load VHDL files.
read_vhdl -vhdl2008 ../rtl/puzzlefw_pkg.vhd
read_vhdl -vhdl2008 ../rtl/simple_fifo.vhd
read_vhdl -vhdl2008 ../rtl/dma_axi_master.vhd
read_vhdl -vhdl2008 ../rtl/dma_write_channel.vhd
read_vhdl -vhdl2008 ../rtl/registers.vhd
read_vhdl -vhdl2008 ../rtl/puzzlefw_top.vhd