Add VHDL for DMA write channel
This commit is contained in:
parent
f58343fc0f
commit
5632ffc6b2
|
@ -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
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,11 +119,19 @@ 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;
|
||||
|
|
|
@ -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;
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue