Add VHDL code
This commit is contained in:
parent
12bcf4e4a9
commit
6b5f2967ac
|
@ -0,0 +1,556 @@
|
||||||
|
--
|
||||||
|
-- AXI3 master for multi-channel DMA controller.
|
||||||
|
--
|
||||||
|
-- Joris van Rantwijk 2024
|
||||||
|
--
|
||||||
|
|
||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
|
use work.puzzlefw_pkg.all;
|
||||||
|
|
||||||
|
|
||||||
|
entity dma_axi_master is
|
||||||
|
|
||||||
|
generic (
|
||||||
|
-- Number of beats per transfer.
|
||||||
|
transfer_size: integer range 1 to 16 := 16;
|
||||||
|
|
||||||
|
-- Number of read channels.
|
||||||
|
num_read_channels: integer range 0 to 16;
|
||||||
|
|
||||||
|
-- Number of write channels.
|
||||||
|
num_write_channels: integer range 0 to 16
|
||||||
|
);
|
||||||
|
|
||||||
|
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 DMA, low to pause DMA.
|
||||||
|
-- When dma_en is low, any ongoing transfer will be completed but no new transfer will be started.
|
||||||
|
dma_en: in std_logic;
|
||||||
|
|
||||||
|
-- High while DMA transactions are in progress.
|
||||||
|
dma_busy: out std_logic;
|
||||||
|
|
||||||
|
-- Address window in which DMA transaction can occur.
|
||||||
|
-- The base address is added to the addresses requested by the channels.
|
||||||
|
window_base_addr: in std_logic_vector(31 downto 12);
|
||||||
|
window_size: in std_logic_vector(31 downto 12);
|
||||||
|
|
||||||
|
-- Error notifications.
|
||||||
|
-- If an error occurs, the corresponding error signal will go high and stay high until cleared.
|
||||||
|
-- After error, further DMA transactions will be halted until the error is cleared.
|
||||||
|
-- Note that DMA errors will typically cause misalignment of the data flow in client channels.
|
||||||
|
err_read: out std_logic; -- AXI slave reported error from read transaction
|
||||||
|
err_write: out std_logic; -- AXI slave reported error from write transaction
|
||||||
|
err_address: out std_logic; -- Channel requested address outside window
|
||||||
|
err_any: out std_logic; -- Logical OR of all error signals
|
||||||
|
|
||||||
|
-- High to clear error notifications.
|
||||||
|
clear_errors: in std_logic;
|
||||||
|
|
||||||
|
-- Read channels.
|
||||||
|
-- The client passes a read command for a specified address via valid/ready handshake.
|
||||||
|
-- Some time later, the controller pushes (transfer_size) data words out to the channel.
|
||||||
|
-- The client must be ready to accept all data words.
|
||||||
|
-- Multiple transfers can be in flight for the channel.
|
||||||
|
read_cmd_addr: in dma_address_array(0 to num_read_channels-1);
|
||||||
|
read_cmd_valid: in std_logic_vector(num_read_channels-1 downto 0);
|
||||||
|
read_cmd_ready: out std_logic_vector(num_read_channels-1 downto 0);
|
||||||
|
read_data: out dma_data_array(0 to num_read_channels-1);
|
||||||
|
read_data_valid: out std_logic_vector(num_read_channels-1 downto 0);
|
||||||
|
|
||||||
|
-- Write channels.
|
||||||
|
-- The client passes a write command for a specified address via valid/ready handshake.
|
||||||
|
-- Some time later, the controller pulls (transfer_size) data words from the channel.
|
||||||
|
-- The client must supply these data words promptly.
|
||||||
|
-- Some more time later, the controller pulses write_finished to indicate that the write has finished.
|
||||||
|
-- Multiple transfers can be in flight for the channel.
|
||||||
|
write_cmd_addr: in dma_address_array(0 to num_write_channels-1);
|
||||||
|
write_cmd_valid: in std_logic_vector(num_write_channels-1 downto 0);
|
||||||
|
write_cmd_ready: out std_logic_vector(num_write_channels-1 downto 0);
|
||||||
|
write_data: in dma_data_array(0 to num_write_channels-1);
|
||||||
|
write_data_ready: out std_logic_vector(num_write_channels-1 downto 0);
|
||||||
|
write_finished: out std_logic_vector(num_write_channels-1 downto 0);
|
||||||
|
|
||||||
|
-- AXI3 master.
|
||||||
|
m_axi_awid: out std_logic_vector(5 downto 0);
|
||||||
|
m_axi_awaddr: out std_logic_vector(31 downto 0);
|
||||||
|
m_axi_awlen: out std_logic_vector(3 downto 0);
|
||||||
|
m_axi_awsize: out std_logic_vector(2 downto 0);
|
||||||
|
m_axi_awburst: out std_logic_vector(1 downto 0);
|
||||||
|
m_axi_awlock: out std_logic_vector(1 downto 0);
|
||||||
|
m_axi_awcache: out std_logic_vector(3 downto 0);
|
||||||
|
m_axi_awprot: out std_logic_vector(2 downto 0);
|
||||||
|
m_axi_awqos: out std_logic_vector(3 downto 0);
|
||||||
|
m_axi_awvalid: out std_logic;
|
||||||
|
m_axi_awready: in std_logic;
|
||||||
|
m_axi_wid: out std_logic_vector(5 downto 0);
|
||||||
|
m_axi_wdata: out std_logic_vector(63 downto 0);
|
||||||
|
m_axi_wstrb: out std_logic_vector(7 downto 0);
|
||||||
|
m_axi_wlast: out std_logic;
|
||||||
|
m_axi_wvalid: out std_logic;
|
||||||
|
m_axi_wready: in std_logic;
|
||||||
|
m_axi_bid: in std_logic_vector(5 downto 0);
|
||||||
|
m_axi_bresp: in std_logic_vector(1 downto 0);
|
||||||
|
m_axi_bvalid: in std_logic;
|
||||||
|
m_axi_bready: out std_logic;
|
||||||
|
m_axi_arid: out std_logic_vector(5 downto 0);
|
||||||
|
m_axi_araddr: out std_logic_vector(31 downto 0);
|
||||||
|
m_axi_arlen: out std_logic_vector(3 downto 0);
|
||||||
|
m_axi_arsize: out std_logic_vector(2 downto 0);
|
||||||
|
m_axi_arburst: out std_logic_vector(1 downto 0);
|
||||||
|
m_axi_arlock: out std_logic_vector(1 downto 0);
|
||||||
|
m_axi_arcache: out std_logic_vector(3 downto 0);
|
||||||
|
m_axi_arprot: out std_logic_vector(2 downto 0);
|
||||||
|
m_axi_arqos: out std_logic_vector(3 downto 0);
|
||||||
|
m_axi_arvalid: out std_logic;
|
||||||
|
m_axi_arready: in std_logic;
|
||||||
|
m_axi_rid: in std_logic_vector(5 downto 0);
|
||||||
|
m_axi_rdata: in std_logic_vector(63 downto 0);
|
||||||
|
m_axi_rresp: in std_logic_vector(1 downto 0);
|
||||||
|
m_axi_rlast: in std_logic;
|
||||||
|
m_axi_rvalid: in std_logic;
|
||||||
|
m_axi_rready: out std_logic
|
||||||
|
);
|
||||||
|
|
||||||
|
end entity;
|
||||||
|
|
||||||
|
architecture arch of dma_axi_master is
|
||||||
|
|
||||||
|
type write_state_type is (WRITE_STATE_IDLE, WRITE_STATE_START, WRITE_STATE_FLOW, WRITE_STATE_WAIT);
|
||||||
|
type read_state_type is (READ_STATE_IDLE, READ_STATE_START, READ_STATE_WAIT);
|
||||||
|
|
||||||
|
type regs_type is record
|
||||||
|
|
||||||
|
-- Registered output signals to AXI bus.
|
||||||
|
awaddr: std_logic_vector(31 downto 3);
|
||||||
|
awvalid: std_logic;
|
||||||
|
wdata: std_logic_vector(63 downto 0);
|
||||||
|
wlast: std_logic;
|
||||||
|
wvalid: std_logic;
|
||||||
|
araddr: std_logic_vector(31 downto 3);
|
||||||
|
arvalid: std_logic;
|
||||||
|
|
||||||
|
-- Registered output signals to read channels.
|
||||||
|
read_cmd_ready: std_logic_vector(num_read_channels-1 downto 0);
|
||||||
|
read_data: std_logic_vector(63 downto 0);
|
||||||
|
read_data_valid: std_logic_vector(num_read_channels-1 downto 0);
|
||||||
|
|
||||||
|
-- Registered output signals to write channels.
|
||||||
|
write_cmd_ready: std_logic_vector(num_write_channels-1 downto 0);
|
||||||
|
write_finished: std_logic_vector(num_write_channels-1 downto 0);
|
||||||
|
|
||||||
|
-- Registered status output signals.
|
||||||
|
dma_busy: std_logic;
|
||||||
|
err_read: std_logic;
|
||||||
|
err_write: std_logic;
|
||||||
|
err_address: std_logic;
|
||||||
|
err_any: std_logic;
|
||||||
|
|
||||||
|
-- Write state machine.
|
||||||
|
write_state: write_state_type;
|
||||||
|
write_channel: integer range 0 to maximum(0, num_write_channels - 1);
|
||||||
|
write_channel_mask: std_logic_vector(num_write_channels-1 downto 0);
|
||||||
|
cnt_write_beat: unsigned(3 downto 0);
|
||||||
|
cnt_write_start: unsigned(5 downto 0);
|
||||||
|
cnt_write_end: unsigned(5 downto 0);
|
||||||
|
|
||||||
|
-- Read command state machine.
|
||||||
|
read_state: read_state_type;
|
||||||
|
read_channel: integer range 0 to maximum(0, num_read_channels - 1);
|
||||||
|
cnt_read_start: unsigned(5 downto 0);
|
||||||
|
cnt_read_end: unsigned(5 downto 0);
|
||||||
|
end record;
|
||||||
|
|
||||||
|
constant regs_init: regs_type := (
|
||||||
|
awaddr => (others => '0'),
|
||||||
|
awvalid => '0',
|
||||||
|
wdata => (others => '0'),
|
||||||
|
wlast => '0',
|
||||||
|
wvalid => '0',
|
||||||
|
araddr => (others => '0'),
|
||||||
|
arvalid => '0',
|
||||||
|
read_cmd_ready => (others => '0'),
|
||||||
|
read_data => (others => '0'),
|
||||||
|
read_data_valid => (others => '0'),
|
||||||
|
write_cmd_ready => (others => '0'),
|
||||||
|
write_finished => (others => '0'),
|
||||||
|
dma_busy => '0',
|
||||||
|
err_read => '0',
|
||||||
|
err_write => '0',
|
||||||
|
err_address => '0',
|
||||||
|
err_any => '0',
|
||||||
|
write_state => WRITE_STATE_IDLE,
|
||||||
|
write_channel => 0,
|
||||||
|
write_channel_mask => (others => '0'),
|
||||||
|
cnt_write_beat => (others => '0'),
|
||||||
|
cnt_write_start => (others => '0'),
|
||||||
|
cnt_write_end => (others => '0'),
|
||||||
|
read_state => READ_STATE_IDLE,
|
||||||
|
read_channel => 0,
|
||||||
|
cnt_read_start => (others => '0'),
|
||||||
|
cnt_read_end => (others => '0')
|
||||||
|
);
|
||||||
|
|
||||||
|
signal r: regs_type := regs_init;
|
||||||
|
signal rnext: regs_type;
|
||||||
|
|
||||||
|
-- Check that the DMA transfer fits inside the address window if it starts at the specified address offset
|
||||||
|
function is_valid_dma_address(addr: std_logic_vector(31 downto 3);
|
||||||
|
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);
|
||||||
|
end function;
|
||||||
|
|
||||||
|
-- Calculate tha AXI address for a DMA transfer by adding the address offset from the client
|
||||||
|
-- to the base address of the DMA window.
|
||||||
|
-- Returns the 29 most significant address bits; the 3 least significant bits are presumed to be 0.
|
||||||
|
function calc_dma_address(addr: std_logic_vector(31 downto 3);
|
||||||
|
base_addr: std_logic_vector(31 downto 12))
|
||||||
|
return std_logic_vector
|
||||||
|
is begin
|
||||||
|
return std_logic_vector(unsigned(addr) + shift_left(resize(unsigned(base_addr), 29), 9));
|
||||||
|
end function;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
-- Drive fixed output signals to AXI bus.
|
||||||
|
m_axi_awlen <= std_logic_vector(to_unsigned(transfer_size - 1, 4)); -- use fixed burst length
|
||||||
|
m_axi_arlen <= std_logic_vector(to_unsigned(transfer_size - 1, 4)); -- use fixed burst length
|
||||||
|
m_axi_awsize <= "011"; -- always use 64-bit transfers
|
||||||
|
m_axi_arsize <= "011"; -- always use 64-bit transfers
|
||||||
|
m_axi_awburst <= "01"; -- always use incrementing burst
|
||||||
|
m_axi_arburst <= "01"; -- always use incrementing burst
|
||||||
|
m_axi_awlock <= "00"; -- normal access
|
||||||
|
m_axi_arlock <= "00"; -- normal access
|
||||||
|
m_axi_awcache <= "0010"; -- normal memory, non-cacheable, non-bufferable
|
||||||
|
m_axi_arcache <= "0010"; -- normal memory, non-cacheable, non-bufferable
|
||||||
|
m_axi_awprot <= "000"; -- data access, secure, unprivileged
|
||||||
|
m_axi_arprot <= "000"; -- data access, secure, unprivileged
|
||||||
|
m_axi_awqos <= "0000"; -- no QoS
|
||||||
|
m_axi_arqos <= "0000"; -- no QoS
|
||||||
|
m_axi_wstrb <= "11111111"; -- always write all byte lanes
|
||||||
|
m_axi_bready <= '1'; -- always ready for write response
|
||||||
|
m_axi_rready <= '1'; -- always ready for read response
|
||||||
|
|
||||||
|
-- Drive variable output signals to AXI bus.
|
||||||
|
m_axi_awid <= std_logic_vector(to_unsigned(r.write_channel, 6));
|
||||||
|
m_axi_awaddr <= r.awaddr & "000"; -- addresses are 8-byte aligned
|
||||||
|
m_axi_awvalid <= r.awvalid;
|
||||||
|
m_axi_wid <= std_logic_vector(to_unsigned(r.write_channel, 6));
|
||||||
|
m_axi_wdata <= r.wdata;
|
||||||
|
m_axi_wlast <= r.wlast;
|
||||||
|
m_axi_wvalid <= r.wvalid;
|
||||||
|
m_axi_arid <= std_logic_vector(to_unsigned(r.read_channel, 6));
|
||||||
|
m_axi_araddr <= r.araddr & "000"; -- addresses are 8-byte aligned
|
||||||
|
m_axi_arvalid <= r.arvalid;
|
||||||
|
|
||||||
|
-- Drive output signals to read channels.
|
||||||
|
read_cmd_ready <= r.read_cmd_ready;
|
||||||
|
read_data_valid <= r.read_data_valid;
|
||||||
|
gen_rdata: for ch in 0 to num_read_channels - 1 generate
|
||||||
|
read_data(ch) <= r.read_data;
|
||||||
|
end generate;
|
||||||
|
|
||||||
|
-- Drive output signals to write channels.
|
||||||
|
write_cmd_ready <= r.write_cmd_ready;
|
||||||
|
write_finished <= r.write_finished;
|
||||||
|
|
||||||
|
-- Write data ready signalling is complicated:
|
||||||
|
-- - During WRITE_STATE_START, one write_data_ready cycle is sent to the selected channel.
|
||||||
|
-- - During WRITE_STATE_FLOW, write_data_ready to the selected channel depends asynchronously on AXI wready.
|
||||||
|
write_data_ready <=
|
||||||
|
r.write_channel_mask
|
||||||
|
when ((r.write_state = WRITE_STATE_START) or (r.write_state = WRITE_STATE_FLOW and m_axi_wready = '1'))
|
||||||
|
else (others => '0');
|
||||||
|
|
||||||
|
-- Drive output signals.
|
||||||
|
dma_busy <= r.dma_busy;
|
||||||
|
err_read <= r.err_read;
|
||||||
|
err_write <= r.err_write;
|
||||||
|
err_address <= r.err_address;
|
||||||
|
err_any <= r.err_any;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Combinatorial process.
|
||||||
|
--
|
||||||
|
process (all) is
|
||||||
|
variable v: regs_type;
|
||||||
|
begin
|
||||||
|
-- Load current register values.
|
||||||
|
v := r;
|
||||||
|
|
||||||
|
-- Report DMA busy/idle.
|
||||||
|
if (r.cnt_write_start = r.cnt_write_end) and (r.cnt_read_start = r.cnt_read_end) then
|
||||||
|
v.dma_busy := '0';
|
||||||
|
else
|
||||||
|
v.dma_busy := '1';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- Clear pending errors.
|
||||||
|
if clear_errors = '1' then
|
||||||
|
v.err_read := '0';
|
||||||
|
v.err_write := '0';
|
||||||
|
v.err_address := '0';
|
||||||
|
v.err_any := '0';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Write state machine.
|
||||||
|
--
|
||||||
|
|
||||||
|
if num_write_channels > 0 then
|
||||||
|
|
||||||
|
-- By default, do not accept write commands from any channel.
|
||||||
|
v.write_cmd_ready := (others => '0');
|
||||||
|
|
||||||
|
-- Maintain one-hot write channel mask for the selected write channel.
|
||||||
|
-- This register is needed during WRITE_STATE_START and WRITE_STATE_FLOW.
|
||||||
|
v.write_channel_mask := (others => '0');
|
||||||
|
v.write_channel_mask(r.write_channel) := '1';
|
||||||
|
|
||||||
|
case r.write_state is
|
||||||
|
when WRITE_STATE_IDLE =>
|
||||||
|
-- Cycle through write channels until we find a channel that wants to write.
|
||||||
|
if (dma_en = '1') and (r.err_any = '0') then
|
||||||
|
if write_cmd_valid(r.write_channel) = '1' then
|
||||||
|
-- This channel wants to read. Let's do it.
|
||||||
|
v.write_state := WRITE_STATE_START;
|
||||||
|
v.write_cmd_ready(r.write_channel) := '1';
|
||||||
|
v.dma_busy := '1';
|
||||||
|
else
|
||||||
|
-- Move on to the next channel.
|
||||||
|
if r.write_channel >= num_write_channels - 1 then
|
||||||
|
v.write_channel := 0;
|
||||||
|
else
|
||||||
|
v.write_channel := r.write_channel + 1;
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
when WRITE_STATE_START =>
|
||||||
|
-- Calculate the AXI address by adding the client address offset to the window base address.
|
||||||
|
v.awaddr := calc_dma_address(write_cmd_addr(r.write_channel), window_base_addr);
|
||||||
|
|
||||||
|
-- Setup first data word.
|
||||||
|
v.wdata := write_data(r.write_channel);
|
||||||
|
v.cnt_write_beat := (others => '0');
|
||||||
|
if transfer_size = 1 then
|
||||||
|
v.wlast := '1';
|
||||||
|
else
|
||||||
|
v.wlast := '0';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- Check address.
|
||||||
|
if is_valid_dma_address(write_cmd_addr(r.write_channel), window_size) then
|
||||||
|
-- Setup AXI write burst.
|
||||||
|
v.awvalid := '1';
|
||||||
|
v.wvalid := '1';
|
||||||
|
v.cnt_write_start := r.cnt_write_start + 1;
|
||||||
|
if transfer_size = 1 then
|
||||||
|
v.write_state := WRITE_STATE_WAIT;
|
||||||
|
else
|
||||||
|
v.write_state := WRITE_STATE_FLOW;
|
||||||
|
end if;
|
||||||
|
else
|
||||||
|
-- Report invalid address.
|
||||||
|
-- At this point, the client channel will be stuck with misaligned write command
|
||||||
|
-- and data channels, and this write transfer will never be confirmed.
|
||||||
|
-- To recover, some type of reset of the client channel will be necessary.
|
||||||
|
v.err_address := '1';
|
||||||
|
v.err_any := '1';
|
||||||
|
v.write_state := WRITE_STATE_IDLE;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- Mark DMA busy.
|
||||||
|
v.dma_busy := '1';
|
||||||
|
|
||||||
|
when WRITE_STATE_FLOW =>
|
||||||
|
-- Push subsequent data words to the interconnect.
|
||||||
|
|
||||||
|
-- Drop write command when accepted by interconnect.
|
||||||
|
if m_axi_awready = '1' then
|
||||||
|
v.awvalid := '0';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- Push data words to interconnect.
|
||||||
|
if m_axi_wready = '1' then
|
||||||
|
v.wdata := write_data(r.write_channel);
|
||||||
|
if r.cnt_write_beat = transfer_size - 2 then
|
||||||
|
-- This will be the last beat of the transfer.
|
||||||
|
v.wlast := '1';
|
||||||
|
v.write_state := WRITE_STATE_WAIT;
|
||||||
|
else
|
||||||
|
v.wlast := '0';
|
||||||
|
end if;
|
||||||
|
v.cnt_write_beat := r.cnt_write_beat + 1;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
when WRITE_STATE_WAIT =>
|
||||||
|
-- Wait until interconnect accepts the last beat.
|
||||||
|
if m_axi_awready = '1' then
|
||||||
|
v.awvalid := '0';
|
||||||
|
end if;
|
||||||
|
if m_axi_wready = '1' then
|
||||||
|
v.wvalid := '0';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
if (r.awvalid = '0' or m_axi_awready = '1') and (r.wvalid = '0' or m_axi_wready = '1') then
|
||||||
|
v.write_state := WRITE_STATE_IDLE;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
end case;
|
||||||
|
|
||||||
|
end if;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Handle write completion.
|
||||||
|
--
|
||||||
|
|
||||||
|
-- Report write completion to the channel client.
|
||||||
|
-- Only successful write bursts are reported.
|
||||||
|
-- Note that a write error will cause misalignment between command and data flow in the channel.
|
||||||
|
for i in 0 to num_write_channels - 1 loop
|
||||||
|
if (m_axi_bvalid = '1') and (m_axi_bresp(1) = '0') and (unsigned(m_axi_bid(3 downto 0)) = i) then
|
||||||
|
v.write_finished(i) := '1';
|
||||||
|
else
|
||||||
|
v.write_finished(i) := '0';
|
||||||
|
end if;
|
||||||
|
end loop;
|
||||||
|
|
||||||
|
-- Detect write errors.
|
||||||
|
if (m_axi_bvalid = '1') and (m_axi_bresp(1) = '1') then
|
||||||
|
v.err_write := '1';
|
||||||
|
v.err_any := '1';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- Count write burst completed.
|
||||||
|
if m_axi_bvalid = '1' then
|
||||||
|
v.cnt_write_end := r.cnt_write_end + 1;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Read command state machine.
|
||||||
|
--
|
||||||
|
|
||||||
|
if num_read_channels > 0 then
|
||||||
|
|
||||||
|
-- By default, do not accept read commands from any channel.
|
||||||
|
v.read_cmd_ready := (others => '0');
|
||||||
|
|
||||||
|
case r.read_state is
|
||||||
|
when READ_STATE_IDLE =>
|
||||||
|
-- Cycle through read channels until we find a channel that wants to read.
|
||||||
|
if (dma_en = '1') and (r.err_any = '0') then
|
||||||
|
if read_cmd_valid(r.read_channel) = '1' then
|
||||||
|
-- This channel wants to read. Let's do it.
|
||||||
|
v.read_state := READ_STATE_START;
|
||||||
|
v.read_cmd_ready(r.read_channel) := '1';
|
||||||
|
v.dma_busy := '1';
|
||||||
|
else
|
||||||
|
-- Move on to the next channel.
|
||||||
|
if r.read_channel >= num_read_channels - 1 then
|
||||||
|
v.read_channel := 0;
|
||||||
|
else
|
||||||
|
v.read_channel := r.read_channel + 1;
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
when READ_STATE_START =>
|
||||||
|
|
||||||
|
-- Calculate the AXI address by adding the client address offset to the window base address.
|
||||||
|
v.araddr := calc_dma_address(read_cmd_addr(r.read_channel), window_base_addr);
|
||||||
|
|
||||||
|
-- Check address.
|
||||||
|
if is_valid_dma_address(read_cmd_addr(r.read_channel), window_size) then
|
||||||
|
-- Setup AXI read burst.
|
||||||
|
v.arvalid := '1';
|
||||||
|
v.read_state := READ_STATE_WAIT;
|
||||||
|
else
|
||||||
|
-- Report invalid address.
|
||||||
|
-- At this point, the client channel will be stuck waiting for results
|
||||||
|
-- that are never going to arrive.
|
||||||
|
-- To recover, some type of reset of the client channel will be necessary.
|
||||||
|
v.err_address := '1';
|
||||||
|
v.err_any := '1';
|
||||||
|
v.read_state := READ_STATE_IDLE;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- Mark DMA busy.
|
||||||
|
v.dma_busy := '1';
|
||||||
|
|
||||||
|
when READ_STATE_WAIT =>
|
||||||
|
-- Wait until interconnect accepts our read burst.
|
||||||
|
v.dma_busy := '1';
|
||||||
|
if m_axi_arready = '1' then
|
||||||
|
-- Read burst accepted.
|
||||||
|
v.arvalid := '0';
|
||||||
|
v.cnt_read_start := r.cnt_read_start + 1;
|
||||||
|
v.read_state := READ_STATE_IDLE;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
end case;
|
||||||
|
|
||||||
|
end if;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Handle read result.
|
||||||
|
--
|
||||||
|
|
||||||
|
-- Latch read result in a register.
|
||||||
|
if m_axi_rvalid = '1' then
|
||||||
|
v.read_data := m_axi_rdata;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- Report read data to the channel client.
|
||||||
|
-- Only successful read results are reported.
|
||||||
|
-- Note than a read error will cause misalignment between command and data flow in the channel.
|
||||||
|
for i in 0 to num_read_channels - 1 loop
|
||||||
|
if (m_axi_rvalid = '1') and (m_axi_rresp(1) = '0') and (unsigned(m_axi_rid(3 downto 0)) = i) then
|
||||||
|
v.read_data_valid(i) := '1';
|
||||||
|
else
|
||||||
|
v.read_data_valid(i) := '0';
|
||||||
|
end if;
|
||||||
|
end loop;
|
||||||
|
|
||||||
|
-- Detect read errors.
|
||||||
|
if (m_axi_rvalid = '1') and (m_axi_rresp(1) = '1') then
|
||||||
|
v.err_read := '1';
|
||||||
|
v.err_any := '1';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- Count read burst completed.
|
||||||
|
if m_axi_rvalid = '1' and m_axi_rlast = '1' then
|
||||||
|
v.cnt_read_end := r.cnt_read_end + 1;
|
||||||
|
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;
|
|
@ -0,0 +1,91 @@
|
||||||
|
--
|
||||||
|
-- Global definitions for Red Pitaya PuzzleFW firmware.
|
||||||
|
--
|
||||||
|
-- Joris van Rantwijk 2024
|
||||||
|
--
|
||||||
|
|
||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
|
|
||||||
|
package puzzlefw_pkg is
|
||||||
|
|
||||||
|
-- 32-bit address for DMA on AXI bus, aligned to 8-byte multiple.
|
||||||
|
subtype dma_address_type is std_logic_vector(31 downto 3);
|
||||||
|
type dma_address_array is array(natural range <>) of dma_address_type;
|
||||||
|
|
||||||
|
-- 64-bit data for DMA on AXI bus.
|
||||||
|
subtype dma_data_type is std_logic_vector(63 downto 0);
|
||||||
|
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_dma_buf_addr: natural := 16#100000#;
|
||||||
|
constant reg_dma_buf_size: natural := 16#100004#;
|
||||||
|
constant reg_test_irq: natural := 16#100100#;
|
||||||
|
constant reg_test_led: natural := 16#100104#;
|
||||||
|
|
||||||
|
-- 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) :=
|
||||||
|
x"4a"
|
||||||
|
& 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_minor, 8));
|
||||||
|
|
||||||
|
-- ADC input port type.
|
||||||
|
type adc_data_input_type is array(0 to 1) of std_logic_vector(15 downto 0);
|
||||||
|
|
||||||
|
-- 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;
|
||||||
|
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
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
end record;
|
||||||
|
|
||||||
|
constant registers_control_init: registers_control := (
|
||||||
|
irq_enable => '0',
|
||||||
|
test_irq => (others => '0'),
|
||||||
|
test_led => (others => '0'),
|
||||||
|
dma_en => '0',
|
||||||
|
dma_buf_addr => (others => '0'),
|
||||||
|
dma_buf_size => (others => '0')
|
||||||
|
);
|
||||||
|
|
||||||
|
constant registers_trigger_init: registers_trigger := (
|
||||||
|
dma_clear => '0',
|
||||||
|
start => '0'
|
||||||
|
);
|
||||||
|
|
||||||
|
end package;
|
|
@ -0,0 +1,440 @@
|
||||||
|
--
|
||||||
|
-- Top-level FPGA design for Red Pitaya PuzzleFW firmware.
|
||||||
|
--
|
||||||
|
-- Joris van Rantwijk 2024
|
||||||
|
--
|
||||||
|
|
||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
|
library unisim;
|
||||||
|
use unisim.vcomponents.all;
|
||||||
|
|
||||||
|
library xpm;
|
||||||
|
use xpm.vcomponents.all;
|
||||||
|
|
||||||
|
use work.puzzlefw_pkg.all;
|
||||||
|
|
||||||
|
|
||||||
|
entity puzzlefw_top is
|
||||||
|
|
||||||
|
port (
|
||||||
|
|
||||||
|
-- Ports directly connected to ARM/PS.
|
||||||
|
DDR_0_addr: inout std_logic_vector(14 downto 0);
|
||||||
|
DDR_0_ba: inout std_logic_vector(2 downto 0);
|
||||||
|
DDR_0_cas_n: inout std_logic;
|
||||||
|
DDR_0_ck_n: inout std_logic;
|
||||||
|
DDR_0_ck_p: inout std_logic;
|
||||||
|
DDR_0_cke: inout std_logic;
|
||||||
|
DDR_0_cs_n: inout std_logic;
|
||||||
|
DDR_0_dm: inout std_logic_vector(3 downto 0);
|
||||||
|
DDR_0_dq: inout std_logic_vector(31 downto 0);
|
||||||
|
DDR_0_dqs_n: inout std_logic_vector(3 downto 0);
|
||||||
|
DDR_0_dqs_p: inout std_logic_vector(3 downto 0);
|
||||||
|
DDR_0_odt: inout std_logic;
|
||||||
|
DDR_0_ras_n: inout std_logic;
|
||||||
|
DDR_0_reset_n: inout std_logic;
|
||||||
|
DDR_0_we_n: inout std_logic;
|
||||||
|
FIXED_IO_0_ddr_vrn: inout std_logic;
|
||||||
|
FIXED_IO_0_ddr_vrp: inout std_logic;
|
||||||
|
FIXED_IO_0_mio: inout std_logic_vector(53 downto 0);
|
||||||
|
FIXED_IO_0_ps_clk: inout std_logic;
|
||||||
|
FIXED_IO_0_ps_porb: inout std_logic;
|
||||||
|
FIXED_IO_0_ps_srstb: inout std_logic;
|
||||||
|
|
||||||
|
-- Ports controlled by FPGA.
|
||||||
|
adc_dat_i: in adc_data_input_type; -- ADC data
|
||||||
|
adc_clk_i: in std_logic_vector(1 downto 0); -- ADC clock 1=pos, 0=neg
|
||||||
|
adc_clk_o: out std_logic_vector(1 downto 0); -- optional clock output for ADC
|
||||||
|
adc_cdcs_o: out std_logic; -- ADC clock duty cycle stabilizer
|
||||||
|
dac_dat_o: out std_logic_vector(13 downto 0); -- DAC data
|
||||||
|
dac_wrt_o: out std_logic; -- DAC write control
|
||||||
|
dac_sel_o: out std_logic; -- DAC channel select
|
||||||
|
dac_clk_o: out std_logic; -- DAC clock
|
||||||
|
dac_rst_o: out std_logic; -- DAC reset
|
||||||
|
dac_pwm_o: out std_logic_vector(3 downto 0); -- PWM DAC
|
||||||
|
exp_p_io: inout std_logic_vector(7 downto 0); -- extension I/O pos
|
||||||
|
exp_n_io: inout std_logic_vector(7 downto 0); -- extension I/O neg
|
||||||
|
led_o: inout std_logic_vector(7 downto 0) -- LEDs
|
||||||
|
);
|
||||||
|
|
||||||
|
end puzzlefw_top;
|
||||||
|
|
||||||
|
architecture arch of puzzlefw_top is
|
||||||
|
|
||||||
|
-- Main 125 MHz clock, derived from ADC clock input port.
|
||||||
|
signal clk_adc: std_logic;
|
||||||
|
|
||||||
|
-- Auxiliary clock from FCLK0.
|
||||||
|
signal clk_fclk: std_logic;
|
||||||
|
|
||||||
|
-- Main reset signal, derived from FCLK_RESET0, active high, synchronous to clk_adc.
|
||||||
|
signal s_reset: std_logic;
|
||||||
|
|
||||||
|
signal s_adc_clk_ibuf: std_logic;
|
||||||
|
signal r_fclk_cnt: unsigned(26 downto 0);
|
||||||
|
signal r_fclk_led: std_logic;
|
||||||
|
signal r_adcclk_cnt: unsigned(26 downto 0);
|
||||||
|
signal r_adcclk_led: std_logic;
|
||||||
|
|
||||||
|
signal s_apb_paddr: std_logic_vector(31 downto 0);
|
||||||
|
signal s_apb_penable: std_logic;
|
||||||
|
signal s_apb_prdata: std_logic_vector(31 downto 0);
|
||||||
|
signal s_apb_pready: std_logic;
|
||||||
|
signal s_apb_psel: std_logic;
|
||||||
|
signal s_apb_pslverr: std_logic;
|
||||||
|
signal s_apb_pwdata: std_logic_vector(31 downto 0);
|
||||||
|
signal s_apb_pwrite: std_logic;
|
||||||
|
|
||||||
|
signal s_axi_awid: std_logic_vector(5 downto 0);
|
||||||
|
signal s_axi_awaddr: std_logic_vector(31 downto 0);
|
||||||
|
signal s_axi_awlen: std_logic_vector(3 downto 0);
|
||||||
|
signal s_axi_awsize: std_logic_vector(2 downto 0);
|
||||||
|
signal s_axi_awburst: std_logic_vector(1 downto 0);
|
||||||
|
signal s_axi_awlock: std_logic_vector(1 downto 0);
|
||||||
|
signal s_axi_awcache: std_logic_vector(3 downto 0);
|
||||||
|
signal s_axi_awprot: std_logic_vector(2 downto 0);
|
||||||
|
signal s_axi_awqos: std_logic_vector(3 downto 0);
|
||||||
|
signal s_axi_awvalid: std_logic;
|
||||||
|
signal s_axi_awready: std_logic;
|
||||||
|
signal s_axi_wid: std_logic_vector(5 downto 0);
|
||||||
|
signal s_axi_wdata: std_logic_vector(63 downto 0);
|
||||||
|
signal s_axi_wstrb: std_logic_vector(7 downto 0);
|
||||||
|
signal s_axi_wlast: std_logic;
|
||||||
|
signal s_axi_wvalid: std_logic;
|
||||||
|
signal s_axi_wready: std_logic;
|
||||||
|
signal s_axi_bid: std_logic_vector(5 downto 0);
|
||||||
|
signal s_axi_bresp: std_logic_vector(1 downto 0);
|
||||||
|
signal s_axi_bvalid: std_logic;
|
||||||
|
signal s_axi_bready: std_logic;
|
||||||
|
signal s_axi_arid: std_logic_vector(5 downto 0);
|
||||||
|
signal s_axi_araddr: std_logic_vector(31 downto 0);
|
||||||
|
signal s_axi_arlen: std_logic_vector(3 downto 0);
|
||||||
|
signal s_axi_arsize: std_logic_vector(2 downto 0);
|
||||||
|
signal s_axi_arburst: std_logic_vector(1 downto 0);
|
||||||
|
signal s_axi_arlock: std_logic_vector(1 downto 0);
|
||||||
|
signal s_axi_arcache: std_logic_vector(3 downto 0);
|
||||||
|
signal s_axi_arprot: std_logic_vector(2 downto 0);
|
||||||
|
signal s_axi_arqos: std_logic_vector(3 downto 0);
|
||||||
|
signal s_axi_arvalid: std_logic;
|
||||||
|
signal s_axi_arready: std_logic;
|
||||||
|
signal s_axi_rid: std_logic_vector(5 downto 0);
|
||||||
|
signal s_axi_rdata: std_logic_vector(63 downto 0);
|
||||||
|
signal s_axi_rresp: std_logic_vector(1 downto 0);
|
||||||
|
signal s_axi_rlast: std_logic;
|
||||||
|
signal s_axi_rvalid: std_logic;
|
||||||
|
signal s_axi_rready: std_logic;
|
||||||
|
|
||||||
|
signal s_irq: 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 r_test_prefetch: std_logic;
|
||||||
|
signal r_test_raddr: std_logic_vector(9 downto 0);
|
||||||
|
signal r_test_waddr: std_logic_vector(9 downto 0);
|
||||||
|
signal s_test_dout: std_logic_vector(63 downto 0);
|
||||||
|
signal s_test_ren: std_logic;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
s_irq <= s_reg_control.test_irq;
|
||||||
|
led_o(7 downto 2) <= s_reg_control.test_led(7 downto 2);
|
||||||
|
|
||||||
|
-- Differential clock input for ADC clock.
|
||||||
|
inst_ibuf_adc_clk: IBUFDS
|
||||||
|
port map (
|
||||||
|
O => s_adc_clk_ibuf,
|
||||||
|
I => adc_clk_i(1),
|
||||||
|
IB => adc_clk_i(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Clock buffer for ADC clock.
|
||||||
|
inst_bufg_adc_clk: BUFG
|
||||||
|
port map (
|
||||||
|
I => s_adc_clk_ibuf,
|
||||||
|
O => clk_adc
|
||||||
|
);
|
||||||
|
|
||||||
|
inst_obuf_led0: OBUF
|
||||||
|
port map (
|
||||||
|
I => r_fclk_led,
|
||||||
|
O => led_o(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
inst_obuf_led1: OBUF
|
||||||
|
port map (
|
||||||
|
I => r_adcclk_led,
|
||||||
|
O => led_o(1)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ARM/PS block design.
|
||||||
|
inst_blockdesign: entity work.puzzlefw_wrapper
|
||||||
|
port map (
|
||||||
|
sys_clk => clk_adc,
|
||||||
|
ps_fclk => clk_fclk,
|
||||||
|
peripheral_reset_0(0) => s_reset,
|
||||||
|
DDR_0_addr => DDR_0_addr,
|
||||||
|
DDR_0_ba => DDR_0_ba,
|
||||||
|
DDR_0_cas_n => DDR_0_cas_n,
|
||||||
|
DDR_0_ck_n => DDR_0_ck_n,
|
||||||
|
DDR_0_ck_p => DDR_0_ck_p,
|
||||||
|
DDR_0_cke => DDR_0_cke,
|
||||||
|
DDR_0_cs_n => DDR_0_cs_n,
|
||||||
|
DDR_0_dm => DDR_0_dm,
|
||||||
|
DDR_0_dq => DDR_0_dq,
|
||||||
|
DDR_0_dqs_n => DDR_0_dqs_n,
|
||||||
|
DDR_0_dqs_p => DDR_0_dqs_p,
|
||||||
|
DDR_0_odt => DDR_0_odt,
|
||||||
|
DDR_0_ras_n => DDR_0_ras_n,
|
||||||
|
DDR_0_reset_n => DDR_0_reset_n,
|
||||||
|
DDR_0_we_n => DDR_0_we_n,
|
||||||
|
FIXED_IO_0_ddr_vrn => FIXED_IO_0_ddr_vrn,
|
||||||
|
FIXED_IO_0_ddr_vrp => FIXED_IO_0_ddr_vrp,
|
||||||
|
FIXED_IO_0_mio => FIXED_IO_0_mio,
|
||||||
|
FIXED_IO_0_ps_clk => FIXED_IO_0_ps_clk,
|
||||||
|
FIXED_IO_0_ps_porb => FIXED_IO_0_ps_porb,
|
||||||
|
FIXED_IO_0_ps_srstb => FIXED_IO_0_ps_srstb,
|
||||||
|
APB_M_0_paddr => s_apb_paddr,
|
||||||
|
APB_M_0_penable => s_apb_penable,
|
||||||
|
APB_M_0_prdata => s_apb_prdata,
|
||||||
|
APB_M_0_pready(0) => s_apb_pready,
|
||||||
|
APB_M_0_psel(0) => s_apb_psel,
|
||||||
|
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,
|
||||||
|
S_AXI_HP0_0_araddr => s_axi_araddr,
|
||||||
|
S_AXI_HP0_0_arburst => s_axi_arburst,
|
||||||
|
S_AXI_HP0_0_arcache => s_axi_arcache,
|
||||||
|
S_AXI_HP0_0_arid => s_axi_arid,
|
||||||
|
S_AXI_HP0_0_arlen => s_axi_arlen,
|
||||||
|
S_AXI_HP0_0_arlock => s_axi_arlock,
|
||||||
|
S_AXI_HP0_0_arprot => s_axi_arprot,
|
||||||
|
S_AXI_HP0_0_arqos => s_axi_arqos,
|
||||||
|
S_AXI_HP0_0_arready => s_axi_arready,
|
||||||
|
S_AXI_HP0_0_arsize => s_axi_arsize,
|
||||||
|
S_AXI_HP0_0_arvalid => s_axi_arvalid,
|
||||||
|
S_AXI_HP0_0_awaddr => s_axi_awaddr,
|
||||||
|
S_AXI_HP0_0_awburst => s_axi_awburst,
|
||||||
|
S_AXI_HP0_0_awcache => s_axi_awcache,
|
||||||
|
S_AXI_HP0_0_awid => s_axi_awid,
|
||||||
|
S_AXI_HP0_0_awlen => s_axi_awlen,
|
||||||
|
S_AXI_HP0_0_awlock => s_axi_awlock,
|
||||||
|
S_AXI_HP0_0_awprot => s_axi_awprot,
|
||||||
|
S_AXI_HP0_0_awqos => s_axi_awqos,
|
||||||
|
S_AXI_HP0_0_awready => s_axi_awready,
|
||||||
|
S_AXI_HP0_0_awsize => s_axi_awsize,
|
||||||
|
S_AXI_HP0_0_awvalid => s_axi_awvalid,
|
||||||
|
S_AXI_HP0_0_bid => s_axi_bid,
|
||||||
|
S_AXI_HP0_0_bready => s_axi_bready,
|
||||||
|
S_AXI_HP0_0_bresp => s_axi_bresp,
|
||||||
|
S_AXI_HP0_0_bvalid => s_axi_bvalid,
|
||||||
|
S_AXI_HP0_0_rdata => s_axi_rdata,
|
||||||
|
S_AXI_HP0_0_rid => s_axi_rid,
|
||||||
|
S_AXI_HP0_0_rlast => s_axi_rlast,
|
||||||
|
S_AXI_HP0_0_rready => s_axi_rready,
|
||||||
|
S_AXI_HP0_0_rresp => s_axi_rresp,
|
||||||
|
S_AXI_HP0_0_rvalid => s_axi_rvalid,
|
||||||
|
S_AXI_HP0_0_wdata => s_axi_wdata,
|
||||||
|
S_AXI_HP0_0_wid => s_axi_wid,
|
||||||
|
S_AXI_HP0_0_wlast => s_axi_wlast,
|
||||||
|
S_AXI_HP0_0_wready => s_axi_wready,
|
||||||
|
S_AXI_HP0_0_wstrb => s_axi_wstrb,
|
||||||
|
S_AXI_HP0_0_wvalid => s_axi_wvalid
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Memory-mapped registers.
|
||||||
|
inst_registers: entity work.registers
|
||||||
|
port map (
|
||||||
|
clk => clk_adc,
|
||||||
|
reset => s_reset,
|
||||||
|
apb_psel => s_apb_psel,
|
||||||
|
apb_penable => s_apb_penable,
|
||||||
|
apb_pwrite => s_apb_pwrite,
|
||||||
|
apb_paddr => s_apb_paddr,
|
||||||
|
apb_pwdata => s_apb_pwdata,
|
||||||
|
apb_pready => s_apb_pready,
|
||||||
|
apb_pslverr => s_apb_pslverr,
|
||||||
|
apb_prdata => s_apb_prdata,
|
||||||
|
reg_control => s_reg_control,
|
||||||
|
reg_status => s_reg_status,
|
||||||
|
reg_trigger => s_reg_trigger
|
||||||
|
);
|
||||||
|
|
||||||
|
-- AXI master.
|
||||||
|
inst_axi_master: entity work.dma_axi_master
|
||||||
|
generic map (
|
||||||
|
transfer_size => 16,
|
||||||
|
num_read_channels => 1,
|
||||||
|
num_write_channels => 1 )
|
||||||
|
port map (
|
||||||
|
clk => clk_adc,
|
||||||
|
reset => s_reset,
|
||||||
|
dma_en => s_reg_control.dma_en,
|
||||||
|
dma_busy => s_reg_status.dma_busy,
|
||||||
|
window_base_addr => s_reg_control.dma_buf_addr,
|
||||||
|
window_size => s_reg_control.dma_buf_size,
|
||||||
|
err_read => s_reg_status.dma_err_read,
|
||||||
|
err_write => s_reg_status.dma_err_write,
|
||||||
|
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,
|
||||||
|
m_axi_awid => s_axi_awid,
|
||||||
|
m_axi_awaddr => s_axi_awaddr,
|
||||||
|
m_axi_awlen => s_axi_awlen,
|
||||||
|
m_axi_awsize => s_axi_awsize,
|
||||||
|
m_axi_awburst => s_axi_awburst,
|
||||||
|
m_axi_awlock => s_axi_awlock,
|
||||||
|
m_axi_awcache => s_axi_awcache,
|
||||||
|
m_axi_awprot => s_axi_awprot,
|
||||||
|
m_axi_awqos => s_axi_awqos,
|
||||||
|
m_axi_awvalid => s_axi_awvalid,
|
||||||
|
m_axi_awready => s_axi_awready,
|
||||||
|
m_axi_wid => s_axi_wid,
|
||||||
|
m_axi_wdata => s_axi_wdata,
|
||||||
|
m_axi_wstrb => s_axi_wstrb,
|
||||||
|
m_axi_wlast => s_axi_wlast,
|
||||||
|
m_axi_wvalid => s_axi_wvalid,
|
||||||
|
m_axi_wready => s_axi_wready,
|
||||||
|
m_axi_bid => s_axi_bid,
|
||||||
|
m_axi_bresp => s_axi_bresp,
|
||||||
|
m_axi_bvalid => s_axi_bvalid,
|
||||||
|
m_axi_bready => s_axi_bready,
|
||||||
|
m_axi_arid => s_axi_arid,
|
||||||
|
m_axi_araddr => s_axi_araddr,
|
||||||
|
m_axi_arlen => s_axi_arlen,
|
||||||
|
m_axi_arsize => s_axi_arsize,
|
||||||
|
m_axi_arburst => s_axi_arburst,
|
||||||
|
m_axi_arlock => s_axi_arlock,
|
||||||
|
m_axi_arcache => s_axi_arcache,
|
||||||
|
m_axi_arprot => s_axi_arprot,
|
||||||
|
m_axi_arqos => s_axi_arqos,
|
||||||
|
m_axi_arvalid => s_axi_arvalid,
|
||||||
|
m_axi_arready => s_axi_arready,
|
||||||
|
m_axi_rid => s_axi_rid,
|
||||||
|
m_axi_rdata => s_axi_rdata,
|
||||||
|
m_axi_rresp => s_axi_rresp,
|
||||||
|
m_axi_rlast => s_axi_rlast,
|
||||||
|
m_axi_rvalid => s_axi_rvalid,
|
||||||
|
m_axi_rready => s_axi_rready
|
||||||
|
);
|
||||||
|
|
||||||
|
-- little test machine for DMA
|
||||||
|
inst_mem: xpm_memory_sdpram
|
||||||
|
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 )
|
||||||
|
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' );
|
||||||
|
|
||||||
|
s_dma_write_data <= not s_test_dout;
|
||||||
|
s_test_ren <= r_test_prefetch or s_dma_write_data_ready;
|
||||||
|
|
||||||
|
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';
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
end process;
|
||||||
|
-- END test
|
||||||
|
|
||||||
|
process (clk_fclk) is
|
||||||
|
begin
|
||||||
|
if rising_edge(clk_fclk) then
|
||||||
|
r_fclk_cnt <= r_fclk_cnt + 1;
|
||||||
|
r_fclk_led <= r_fclk_cnt(r_fclk_cnt'high);
|
||||||
|
end if;
|
||||||
|
end process;
|
||||||
|
|
||||||
|
process (clk_adc) is
|
||||||
|
begin
|
||||||
|
if rising_edge(clk_adc) then
|
||||||
|
r_adcclk_cnt <= r_adcclk_cnt + 1;
|
||||||
|
r_adcclk_led <= r_adcclk_cnt(r_adcclk_cnt'high);
|
||||||
|
end if;
|
||||||
|
end process;
|
||||||
|
|
||||||
|
end architecture;
|
|
@ -0,0 +1,144 @@
|
||||||
|
--
|
||||||
|
-- Memory-mapped registers for Red Pitaya PuzzleFW firmware.
|
||||||
|
--
|
||||||
|
-- Joris van Rantwijk 2024
|
||||||
|
--
|
||||||
|
|
||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
|
use work.puzzlefw_pkg.all;
|
||||||
|
|
||||||
|
|
||||||
|
entity registers is
|
||||||
|
|
||||||
|
port (
|
||||||
|
-- Main clock, active on rising edge.
|
||||||
|
clk: in std_logic;
|
||||||
|
|
||||||
|
-- Reset, active high, synchronous to main clock.
|
||||||
|
reset: in std_logic;
|
||||||
|
|
||||||
|
-- APB bus interface.
|
||||||
|
apb_psel: in std_logic;
|
||||||
|
apb_penable: in std_logic;
|
||||||
|
apb_pwrite: in std_logic;
|
||||||
|
apb_paddr: in std_logic_vector(31 downto 0);
|
||||||
|
apb_pwdata: in std_logic_vector(31 downto 0);
|
||||||
|
apb_pready: out std_logic;
|
||||||
|
apb_pslverr: out std_logic;
|
||||||
|
apb_prdata: out std_logic_vector(31 downto 0);
|
||||||
|
|
||||||
|
-- FPGA interface.
|
||||||
|
reg_control: out registers_control;
|
||||||
|
reg_status: in registers_status;
|
||||||
|
reg_trigger: out registers_trigger
|
||||||
|
);
|
||||||
|
|
||||||
|
end entity;
|
||||||
|
|
||||||
|
architecture arch of registers is
|
||||||
|
|
||||||
|
type regs_type is record
|
||||||
|
pready: std_logic;
|
||||||
|
prdata: std_logic_vector(31 downto 0);
|
||||||
|
reg_control: registers_control;
|
||||||
|
reg_trigger: registers_trigger;
|
||||||
|
end record;
|
||||||
|
|
||||||
|
constant regs_init: regs_type := (
|
||||||
|
pready => '0',
|
||||||
|
prdata => (others => '0'),
|
||||||
|
reg_control => registers_control_init,
|
||||||
|
reg_trigger => registers_trigger_init
|
||||||
|
);
|
||||||
|
|
||||||
|
signal r: regs_type := regs_init;
|
||||||
|
signal rnext: regs_type;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
-- Drive output signals.
|
||||||
|
apb_pready <= r.pready;
|
||||||
|
apb_pslverr <= '0'; -- never signal errors
|
||||||
|
apb_prdata <= r.prdata;
|
||||||
|
|
||||||
|
reg_control <= r.reg_control;
|
||||||
|
reg_trigger <= r.reg_trigger;
|
||||||
|
|
||||||
|
-- Combinatorial process.
|
||||||
|
process (all) is
|
||||||
|
variable v: regs_type;
|
||||||
|
begin
|
||||||
|
-- Load current register values.
|
||||||
|
v := r;
|
||||||
|
|
||||||
|
-- Clear single-cycle trigger pulses.
|
||||||
|
v.reg_trigger := registers_trigger_init;
|
||||||
|
|
||||||
|
-- Respond to each APB access on the next clock cycle (no wait states).
|
||||||
|
v.pready := apb_psel and (not apb_penable);
|
||||||
|
|
||||||
|
-- Handle APB read transfer.
|
||||||
|
if apb_psel = '1' and apb_penable = '0' and apb_pwrite = '0' then
|
||||||
|
|
||||||
|
-- Initialize result to all-zero bits.
|
||||||
|
v.prdata := (others => '0');
|
||||||
|
|
||||||
|
-- Determine read result as function of address.
|
||||||
|
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_dma_en => v.prdata(0) := r.reg_control.dma_en;
|
||||||
|
when reg_dma_status =>
|
||||||
|
v.prdata(0) := reg_status.dma_busy;
|
||||||
|
v.prdata(1) := reg_status.dma_err_read;
|
||||||
|
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_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;
|
||||||
|
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- Handle APB write transfer.
|
||||||
|
if apb_psel = '1' and apb_penable = '0' and apb_pwrite = '1' then
|
||||||
|
case to_integer(unsigned(apb_paddr and reg_addr_mask)) is
|
||||||
|
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_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;
|
||||||
|
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;
|
Loading…
Reference in New Issue