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))
|
limit: std_logic_vector(31 downto 12))
|
||||||
return boolean
|
return boolean
|
||||||
is begin
|
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;
|
end function;
|
||||||
|
|
||||||
-- Calculate tha AXI address for a DMA transfer by adding the address offset from the client
|
-- 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;
|
type dma_data_array is array(natural range <>) of dma_data_type;
|
||||||
|
|
||||||
-- Register addresses.
|
-- Register addresses.
|
||||||
constant reg_addr_mask: std_logic_vector(31 downto 0) := x"0010fffc";
|
constant reg_addr_mask: std_logic_vector(31 downto 0) := x"0010fffc";
|
||||||
constant reg_info: natural := 16#000000#;
|
constant reg_info: natural := 16#000000#;
|
||||||
constant reg_irq_enable: natural := 16#000010#;
|
constant reg_irq_enable: natural := 16#000010#;
|
||||||
constant reg_dma_en: natural := 16#000100#;
|
constant reg_irq_pending: natural := 16#000014#;
|
||||||
constant reg_dma_status: natural := 16#000104#;
|
constant reg_dma_en: natural := 16#000100#;
|
||||||
constant reg_dma_clear: natural := 16#000108#;
|
constant reg_dma_status: natural := 16#000104#;
|
||||||
constant reg_rcnt: natural := 16#000200#;
|
constant reg_dma_clear: natural := 16#000108#;
|
||||||
constant reg_wcnt: natural := 16#000204#;
|
constant reg_acq_addr_start: natural := 16#000200#;
|
||||||
constant reg_start: natural := 16#000208#;
|
constant reg_acq_addr_end: natural := 16#000204#;
|
||||||
constant reg_test_irq: natural := 16#000400#;
|
constant reg_acq_addr_limit: natural := 16#000208#;
|
||||||
constant reg_test_led: natural := 16#000404#;
|
constant reg_acq_addr_intr: natural := 16#00020c#;
|
||||||
constant reg_dma_buf_addr: natural := 16#100000#;
|
constant reg_acq_addr_ptr: natural := 16#000210#;
|
||||||
constant reg_dma_buf_size: natural := 16#100004#;
|
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.
|
-- Firmware info word.
|
||||||
constant fw_api_version: natural := 1;
|
constant fw_api_version: natural := 1;
|
||||||
constant fw_version_major: natural := 0;
|
constant fw_version_major: natural := 0;
|
||||||
constant fw_version_minor: natural := 2;
|
constant fw_version_minor: natural := 3;
|
||||||
constant fw_info_word: std_logic_vector(31 downto 0) :=
|
constant fw_info_word: std_logic_vector(31 downto 0) :=
|
||||||
x"4a"
|
x"4a"
|
||||||
& std_logic_vector(to_unsigned(fw_api_version, 8))
|
& std_logic_vector(to_unsigned(fw_api_version, 8))
|
||||||
& std_logic_vector(to_unsigned(fw_version_major, 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.
|
-- Control registers: read/write access by processor, output signals to FPGA.
|
||||||
type registers_control is record
|
type registers_control is record
|
||||||
irq_enable: std_logic;
|
irq_enable: std_logic;
|
||||||
test_irq: std_logic_vector(7 downto 0);
|
|
||||||
test_led: std_logic_vector(7 downto 0);
|
test_led: std_logic_vector(7 downto 0);
|
||||||
dma_en: std_logic;
|
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_addr: std_logic_vector(31 downto 12);
|
||||||
dma_buf_size: std_logic_vector(31 downto 12);
|
dma_buf_size: std_logic_vector(31 downto 12);
|
||||||
end record;
|
end record;
|
||||||
|
|
||||||
-- Status registers: input signals from FPGA, read-only access by processor.
|
-- Status registers: input signals from FPGA, read-only access by processor.
|
||||||
type registers_status is record
|
type registers_status is record
|
||||||
|
irq_pending: std_logic_vector(0 downto 0);
|
||||||
dma_busy: std_logic;
|
dma_busy: std_logic;
|
||||||
dma_err_read: std_logic;
|
dma_err_read: std_logic;
|
||||||
dma_err_write: std_logic;
|
dma_err_write: std_logic;
|
||||||
dma_err_address: std_logic;
|
dma_err_address: std_logic;
|
||||||
dma_err_any: std_logic;
|
dma_err_any: std_logic;
|
||||||
rcnt: unsigned(31 downto 0);
|
acq_addr_ptr: std_logic_vector(31 downto 7);
|
||||||
wcnt: unsigned(31 downto 0);
|
|
||||||
end record;
|
end record;
|
||||||
|
|
||||||
-- Trigger registers: write-only access by processor, single-cycle pulse signals to FPGA.
|
-- Trigger registers: write-only access by processor, single-cycle pulse signals to FPGA.
|
||||||
type registers_trigger is record
|
type registers_trigger is record
|
||||||
dma_clear: std_logic;
|
dma_clear: std_logic;
|
||||||
start: std_logic;
|
acq_channel_init: std_logic;
|
||||||
|
acq_intr_clear: std_logic;
|
||||||
end record;
|
end record;
|
||||||
|
|
||||||
constant registers_control_init: registers_control := (
|
constant registers_control_init: registers_control := (
|
||||||
irq_enable => '0',
|
irq_enable => '0',
|
||||||
test_irq => (others => '0'),
|
|
||||||
test_led => (others => '0'),
|
test_led => (others => '0'),
|
||||||
dma_en => '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_addr => (others => '0'),
|
||||||
dma_buf_size => (others => '0')
|
dma_buf_size => (others => '0')
|
||||||
);
|
);
|
||||||
|
|
||||||
constant registers_trigger_init: registers_trigger := (
|
constant registers_trigger_init: registers_trigger := (
|
||||||
dma_clear => '0',
|
dma_clear => '0',
|
||||||
start => '0'
|
acq_channel_init => '0',
|
||||||
|
acq_intr_clear => '0'
|
||||||
);
|
);
|
||||||
|
|
||||||
end package;
|
end package;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
library ieee;
|
library ieee;
|
||||||
use ieee.std_logic_1164.all;
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.std_logic_misc.all;
|
||||||
use ieee.numeric_std.all;
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
library unisim;
|
library unisim;
|
||||||
|
@ -127,23 +128,24 @@ architecture arch of puzzlefw_top is
|
||||||
signal s_axi_rvalid: std_logic;
|
signal s_axi_rvalid: std_logic;
|
||||||
signal s_axi_rready: 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_control: registers_control;
|
||||||
signal s_reg_status: registers_status;
|
signal s_reg_status: registers_status;
|
||||||
signal s_reg_trigger: registers_trigger;
|
signal s_reg_trigger: registers_trigger;
|
||||||
|
|
||||||
signal s_dma_read_cmd_addr: dma_address_type;
|
signal s_dma_write_cmd_addr: dma_address_array(0 downto 0);
|
||||||
signal s_dma_read_cmd_valid: std_logic;
|
signal s_dma_write_cmd_valid: std_logic_vector(0 downto 0);
|
||||||
signal s_dma_read_cmd_ready: std_logic;
|
signal s_dma_write_cmd_ready: std_logic_vector(0 downto 0);
|
||||||
signal s_dma_read_data: dma_data_type;
|
signal s_dma_write_data: dma_data_array(0 downto 0);
|
||||||
signal s_dma_read_data_valid: std_logic;
|
signal s_dma_write_data_ready: std_logic_vector(0 downto 0);
|
||||||
signal s_dma_write_cmd_addr: dma_address_type;
|
signal s_dma_write_finished: std_logic_vector(0 downto 0);
|
||||||
signal s_dma_write_cmd_valid: std_logic;
|
|
||||||
signal s_dma_write_cmd_ready: std_logic;
|
signal s_dma_in_valid: std_logic;
|
||||||
signal s_dma_write_data: dma_data_type;
|
signal s_dma_in_ready: std_logic;
|
||||||
signal s_dma_write_data_ready: std_logic;
|
signal s_dma_in_data: std_logic_vector(63 downto 0);
|
||||||
signal s_dma_write_finished: std_logic;
|
|
||||||
|
|
||||||
signal r_test_prefetch: std_logic;
|
signal r_test_prefetch: std_logic;
|
||||||
signal r_test_raddr: std_logic_vector(9 downto 0);
|
signal r_test_raddr: std_logic_vector(9 downto 0);
|
||||||
|
@ -153,8 +155,6 @@ architecture arch of puzzlefw_top is
|
||||||
|
|
||||||
begin
|
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);
|
led_o(7 downto 2) <= s_reg_control.test_led(7 downto 2);
|
||||||
|
|
||||||
-- Differential clock input for ADC clock.
|
-- Differential clock input for ADC clock.
|
||||||
|
@ -219,7 +219,7 @@ begin
|
||||||
APB_M_0_pslverr(0) => s_apb_pslverr,
|
APB_M_0_pslverr(0) => s_apb_pslverr,
|
||||||
APB_M_0_pwdata => s_apb_pwdata,
|
APB_M_0_pwdata => s_apb_pwdata,
|
||||||
APB_M_0_pwrite => s_apb_pwrite,
|
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_araddr => s_axi_araddr,
|
||||||
S_AXI_HP0_0_arburst => s_axi_arburst,
|
S_AXI_HP0_0_arburst => s_axi_arburst,
|
||||||
S_AXI_HP0_0_arcache => s_axi_arcache,
|
S_AXI_HP0_0_arcache => s_axi_arcache,
|
||||||
|
@ -282,7 +282,7 @@ begin
|
||||||
inst_axi_master: entity work.dma_axi_master
|
inst_axi_master: entity work.dma_axi_master
|
||||||
generic map (
|
generic map (
|
||||||
transfer_size => 16,
|
transfer_size => 16,
|
||||||
num_read_channels => 1,
|
num_read_channels => 0,
|
||||||
num_write_channels => 1 )
|
num_write_channels => 1 )
|
||||||
port map (
|
port map (
|
||||||
clk => clk_adc,
|
clk => clk_adc,
|
||||||
|
@ -296,17 +296,17 @@ begin
|
||||||
err_address => s_reg_status.dma_err_address,
|
err_address => s_reg_status.dma_err_address,
|
||||||
err_any => s_reg_status.dma_err_any,
|
err_any => s_reg_status.dma_err_any,
|
||||||
clear_errors => s_reg_trigger.dma_clear,
|
clear_errors => s_reg_trigger.dma_clear,
|
||||||
read_cmd_addr(0) => s_dma_read_cmd_addr,
|
read_cmd_addr => (others => (others => '0')),
|
||||||
read_cmd_valid(0) => s_dma_read_cmd_valid,
|
read_cmd_valid => (others => '0'),
|
||||||
read_cmd_ready(0) => s_dma_read_cmd_ready,
|
read_cmd_ready => open,
|
||||||
read_data(0) => s_dma_read_data,
|
read_data => open,
|
||||||
read_data_valid(0) => s_dma_read_data_valid,
|
read_data_valid => open,
|
||||||
write_cmd_addr(0) => s_dma_write_cmd_addr,
|
write_cmd_addr => s_dma_write_cmd_addr,
|
||||||
write_cmd_valid(0) => s_dma_write_cmd_valid,
|
write_cmd_valid => s_dma_write_cmd_valid,
|
||||||
write_cmd_ready(0) => s_dma_write_cmd_ready,
|
write_cmd_ready => s_dma_write_cmd_ready,
|
||||||
write_data(0) => s_dma_write_data,
|
write_data => s_dma_write_data,
|
||||||
write_data_ready(0) => s_dma_write_data_ready,
|
write_data_ready => s_dma_write_data_ready,
|
||||||
write_finished(0) => s_dma_write_finished,
|
write_finished => s_dma_write_finished,
|
||||||
m_axi_awid => s_axi_awid,
|
m_axi_awid => s_axi_awid,
|
||||||
m_axi_awaddr => s_axi_awaddr,
|
m_axi_awaddr => s_axi_awaddr,
|
||||||
m_axi_awlen => s_axi_awlen,
|
m_axi_awlen => s_axi_awlen,
|
||||||
|
@ -347,76 +347,47 @@ begin
|
||||||
m_axi_rready => s_axi_rready
|
m_axi_rready => s_axi_rready
|
||||||
);
|
);
|
||||||
|
|
||||||
-- little test machine for DMA
|
-- DMA Write Channel
|
||||||
inst_mem: xpm_memory_sdpram
|
inst_write_channel: entity work.dma_write_channel
|
||||||
generic map (
|
generic map (
|
||||||
CLOCKING_MODE => "common_clock",
|
queue_size_bits => 10 )
|
||||||
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 )
|
|
||||||
port map (
|
port map (
|
||||||
clka => clk_adc,
|
clk => clk_adc,
|
||||||
clkb => clk_adc,
|
reset => s_reset,
|
||||||
rstb => s_reset,
|
channel_en => s_reg_control.acq_channel_en,
|
||||||
addra => r_test_waddr,
|
channel_init => s_reg_trigger.acq_channel_init,
|
||||||
addrb => r_test_raddr,
|
addr_start => s_reg_control.acq_addr_start,
|
||||||
dina => s_dma_read_data,
|
addr_end => s_reg_control.acq_addr_end,
|
||||||
doutb => s_test_dout,
|
addr_limit => s_reg_control.acq_addr_limit,
|
||||||
ena => s_dma_read_data_valid,
|
addr_interrupt => s_reg_control.acq_addr_intr,
|
||||||
enb => s_test_ren,
|
addr_pointer => s_reg_status.acq_addr_ptr,
|
||||||
wea => "1",
|
intr_en => s_reg_control.acq_intr_en,
|
||||||
regceb => '1',
|
intr_clear => s_reg_trigger.acq_intr_clear,
|
||||||
injectdbiterra => '0',
|
intr_out => s_dma_interrupt,
|
||||||
injectsbiterra => '0',
|
in_valid => s_dma_in_valid,
|
||||||
sleep => '0' );
|
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;
|
-- Collect interrupt signals from peripherals and generate interrupt to PS.
|
||||||
s_test_ren <= r_test_prefetch or s_dma_write_data_ready;
|
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
|
process (clk_adc) is
|
||||||
begin
|
begin
|
||||||
if rising_edge(clk_adc) then
|
if rising_edge(clk_adc) then
|
||||||
r_test_prefetch <= '0';
|
s_dma_in_valid <= '1';
|
||||||
if s_dma_read_cmd_ready = '1' and s_dma_read_cmd_valid = '1' then
|
if s_dma_in_ready = '1' then
|
||||||
if unsigned(s_dma_read_cmd_addr) < 1008 then
|
s_dma_in_data(15 downto 0) <= std_logic_vector(unsigned(s_dma_in_data(15 downto 0)) + 1);
|
||||||
s_dma_read_cmd_addr <= std_logic_vector(unsigned(s_dma_read_cmd_addr) + 16);
|
s_dma_in_data(63 downto 16) <= std_logic_vector(unsigned(s_dma_in_data(63 downto 16)) - 1);
|
||||||
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';
|
|
||||||
end if;
|
end if;
|
||||||
end if;
|
end if;
|
||||||
end process;
|
end process;
|
||||||
|
|
|
@ -90,6 +90,7 @@ begin
|
||||||
case to_integer(unsigned(apb_paddr and reg_addr_mask)) is
|
case to_integer(unsigned(apb_paddr and reg_addr_mask)) is
|
||||||
when reg_info => v.prdata := fw_info_word;
|
when reg_info => v.prdata := fw_info_word;
|
||||||
when reg_irq_enable => v.prdata(0) := r.reg_control.irq_enable;
|
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_en => v.prdata(0) := r.reg_control.dma_en;
|
||||||
when reg_dma_status =>
|
when reg_dma_status =>
|
||||||
v.prdata(0) := reg_status.dma_busy;
|
v.prdata(0) := reg_status.dma_busy;
|
||||||
|
@ -97,12 +98,16 @@ begin
|
||||||
v.prdata(2) := reg_status.dma_err_write;
|
v.prdata(2) := reg_status.dma_err_write;
|
||||||
v.prdata(3) := reg_status.dma_err_address;
|
v.prdata(3) := reg_status.dma_err_address;
|
||||||
v.prdata(4) := reg_status.dma_err_any;
|
v.prdata(4) := reg_status.dma_err_any;
|
||||||
when reg_rcnt => v.prdata := std_logic_vector(reg_status.rcnt);
|
when reg_acq_addr_start => v.prdata(31 downto 7) := r.reg_control.acq_addr_start;
|
||||||
when reg_wcnt => v.prdata := std_logic_vector(reg_status.wcnt);
|
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_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_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;
|
when others => null;
|
||||||
end case;
|
end case;
|
||||||
|
|
||||||
|
@ -114,11 +119,19 @@ begin
|
||||||
when reg_irq_enable => v.reg_control.irq_enable := apb_pwdata(0);
|
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_en => v.reg_control.dma_en := apb_pwdata(0);
|
||||||
when reg_dma_clear => v.reg_trigger.dma_clear := 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_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_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;
|
when others => null;
|
||||||
end case;
|
end case;
|
||||||
end if;
|
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.
|
# Load VHDL files.
|
||||||
read_vhdl -vhdl2008 ../rtl/puzzlefw_pkg.vhd
|
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_axi_master.vhd
|
||||||
|
read_vhdl -vhdl2008 ../rtl/dma_write_channel.vhd
|
||||||
read_vhdl -vhdl2008 ../rtl/registers.vhd
|
read_vhdl -vhdl2008 ../rtl/registers.vhd
|
||||||
read_vhdl -vhdl2008 ../rtl/puzzlefw_top.vhd
|
read_vhdl -vhdl2008 ../rtl/puzzlefw_top.vhd
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue