redpitaya-puzzlefw/fpga/rtl/registers.vhd

145 lines
5.0 KiB
VHDL
Raw Normal View History

2024-08-02 21:47:58 +02:00
--
-- 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;