URL
https://opencores.org/ocsvn/spacewire_light/spacewire_light/trunk
Subversion Repositories spacewire_light
Compare Revisions
- This comparison shows the changes necessary to convert path
/spacewire_light/trunk/rtl/vhdl
- from Rev 4 to Rev 5
- ↔ Reverse comparison
Rev 4 → Rev 5
/spwstream.vhd
402,17 → 402,7
-- send character |
v.txpacket := not s_txfifo_rdata(8); |
end if; |
if linko.running = '0' then |
-- not connected |
v.rxpacket := '0'; |
v.txpacket := '0'; |
end if; |
|
-- Clear the discard flag when the link is explicitly disabled. |
if linkdis = '1' then |
v.txdiscard := '0'; |
end if; |
|
-- Update RX fifo pointers. |
if (rxread = '1') and (r.rxfifo_rvalid = '1') then |
-- read from fifo |
462,9 → 452,8
v.txfull := bool_to_logic(v_tmptxroom = 0); |
v.txhalff := not v_tmptxroom(v_tmptxroom'high); |
|
-- If an error occurs, set a flag to discard the current packet. |
if (linko.errdisc or linko.errpar or |
linko.erresc or linko.errcred) = '1' then |
-- If the link is lost, set a flag to discard the current packet. |
if linko.running = '0' then |
v.rxeep := v.rxeep or v.rxpacket; -- use new value of rxpacket |
v.txdiscard := v.txdiscard or v.txpacket; -- use new value of txpacket |
v.rxpacket := '0'; |
471,6 → 460,11
v.txpacket := '0'; |
end if; |
|
-- Clear the discard flag when the link is explicitly disabled. |
if linkdis = '1' then |
v.txdiscard := '0'; |
end if; |
|
-- Drive control signals to RX fifo. |
s_rxfifo_raddr <= v.rxfifo_raddr; -- using new value of rxfifo_raddr |
s_rxfifo_wen <= (not r.rxfull) and (linko.rxchar or r.rxeep); |
/spwahbmst.vhd
0,0 → 1,735
-- |
-- AHB master for AMBA interface. |
-- |
-- This is a helper entity for the SpaceWire AMBA interface. |
-- It implements the AHB master which transfers data from/to main memory. |
-- |
-- Descriptor flag bits on input: |
-- bit 15:0 (RX) max nr of bytes to receive (must be a multiple of 4) |
-- (TX) nr of bytes to transmit |
-- bit 16 EN: '1' = descriptor enabled |
-- bit 17 WR: wrap to beginning of descriptor table |
-- bit 18 IE: interrupt at end of descriptor |
-- bit 19 '0' |
-- bit 20 (TX only) send EOP after end of data |
-- bit 21 (TX only) send EEP after end of data |
-- |
-- Descriptor flag bits after completion of frame: |
-- bit 15:0 (RX only) LEN: nr of bytes received |
-- (TX) undefined |
-- bit 16 '0' |
-- bit 18:17 undefined |
-- bit 19 '1' to indicate descriptor completed |
--- bit 20 (RX only) received EOP after end of data |
-- bit 21 (RX only) received EEP after end of data |
-- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
library grlib; |
use grlib.amba.all; |
use grlib.stdlib.all; |
use work.spwambapkg.all; |
|
|
entity spwahbmst is |
|
generic ( |
-- AHB master index. |
hindex: integer; |
|
-- AHB plug&play information. |
hconfig: ahb_config_type; |
|
-- Maximum burst length as the 2-logarithm of the number of words. |
maxburst: integer range 1 to 8 |
); |
|
port ( |
-- System clock. |
clk: in std_logic; |
|
-- Synchronous reset (active-low). |
rstn: in std_logic; |
|
-- Inputs from SpaceWire core. |
msti: in spw_ahbmst_in_type; |
|
-- Outputs to SpaceWire core. |
msto: out spw_ahbmst_out_type; |
|
-- AHB master input signals. |
ahbi: in ahb_mst_in_type; |
|
-- AHB master output signals. |
ahbo: out ahb_mst_out_type |
); |
|
end entity spwahbmst; |
|
architecture spwahbmst_arch of spwahbmst is |
|
-- |
-- Registers. |
-- |
|
type state_type is ( |
st_idle, |
st_rxgetdesc, st_rxgetptr, st_rxtransfer, st_rxfinal, st_rxputdesc, |
st_txgetdesc, st_txgetptr, st_txtransfer, st_txfinal, st_txputdesc, st_txskip ); |
|
type burst_state_type is ( bs_idle, bs_setup, bs_active, bs_end ); |
|
type regs_type is record |
-- dma state |
rxdma_act: std_ulogic; |
txdma_act: std_ulogic; |
ahberror: std_ulogic; |
-- main state machine |
mstate: state_type; |
firstword: std_ulogic; |
prefertx: std_ulogic; |
-- rx descriptor state |
rxdes_en: std_ulogic; |
rxdes_wr: std_ulogic; |
rxdes_ie: std_ulogic; |
rxdes_eop: std_ulogic; |
rxdes_eep: std_ulogic; |
rxdes_len: std_logic_vector(13 downto 0); -- in 32-bit words |
rxdes_pos: std_logic_vector(15 downto 0); -- in bytes |
rxaddr: std_logic_vector(31 downto 2); |
rxdesc_next: std_ulogic; |
-- tx descriptor state |
txdes_en: std_ulogic; |
txdes_wr: std_ulogic; |
txdes_ie: std_ulogic; |
txdes_eop: std_ulogic; |
txdes_eep: std_ulogic; |
txdes_len: std_logic_vector(15 downto 0); -- in bytes |
txaddr: std_logic_vector(31 downto 2); |
txdesc_next: std_ulogic; |
-- interrupts |
int_rxdesc: std_ulogic; |
int_txdesc: std_ulogic; |
int_rxpacket: std_ulogic; |
-- burst state |
burststat: burst_state_type; |
hbusreq: std_ulogic; |
hwrite: std_ulogic; |
haddr: std_logic_vector(31 downto 2); |
hwdata: std_logic_vector(31 downto 0); |
end record; |
|
constant regs_reset: regs_type := ( |
rxdma_act => '0', |
txdma_act => '0', |
ahberror => '0', |
mstate => st_idle, |
firstword => '0', |
prefertx => '0', |
rxdes_en => '0', |
rxdes_wr => '0', |
rxdes_ie => '0', |
rxdes_eop => '0', |
rxdes_eep => '0', |
rxdes_len => (others => '0'), |
rxdes_pos => (others => '0'), |
rxaddr => (others => '0'), |
rxdesc_next => '0', |
txdes_en => '0', |
txdes_wr => '0', |
txdes_ie => '0', |
txdes_eop => '0', |
txdes_eep => '0', |
txdes_len => (others => '0'), |
txaddr => (others => '0'), |
txdesc_next => '0', |
int_rxdesc => '0', |
int_txdesc => '0', |
int_rxpacket => '0', |
burststat => bs_idle, |
hbusreq => '0', |
hwrite => '0', |
haddr => (others => '0'), |
hwdata => (others => '0') ); |
|
signal r: regs_type := regs_reset; |
signal rin: regs_type; |
|
begin |
|
-- |
-- Combinatorial process |
-- |
process (r, rstn, msti, ahbi) is |
variable v: regs_type; |
variable v_burstreq: std_logic; |
variable v_burstack: std_logic; |
variable v_rxfifo_read: std_logic; |
variable v_txfifo_write: std_logic; |
variable v_txfifo_wdata: std_logic_vector(35 downto 0); |
begin |
v := r; |
|
-- Assume no burst request. |
v_burstreq := '0'; |
|
-- Detect request from burst state machine for next data word. |
v_burstack := ahbi.hready and |
conv_std_logic(r.burststat = bs_active or r.burststat = bs_end); |
|
-- Assume no fifo activity; take data for TX fifo from AHB bus. |
v_rxfifo_read := '0'; |
v_txfifo_write := '0'; |
v_txfifo_wdata(35 downto 32) := (others => '0'); |
v_txfifo_wdata(31 downto 0) := ahbi.hrdata; |
|
-- Reset registers for interrupts and descriptor updates. |
v.int_rxdesc := '0'; |
v.int_txdesc := '0'; |
v.int_rxpacket := '0'; |
v.rxdesc_next := '0'; |
v.txdesc_next := '0'; |
|
-- Start DMA on external request. |
if msti.rxdma_start = '1' then v.rxdma_act := '1'; end if; |
if msti.txdma_start = '1' then v.txdma_act := '1'; end if; |
|
-- |
-- Main state machine. |
-- |
case r.mstate is |
|
when st_idle => |
-- Waiting for something to do. |
v.prefertx := '0'; |
v.firstword := '1'; |
if msti.txdma_cancel = '1' then |
v.txdma_act := '0'; |
v.txdes_en := '0'; |
end if; |
if r.rxdma_act = '1' and msti.rxfifo_empty = '0' and |
(r.prefertx = '0' or r.txdma_act = '0' or msti.txfifo_highw = '1') then |
-- Start RX transfer. |
if r.rxdes_en = '1' then |
-- Transfer RX data to current descriptor. |
v_burstreq := '1'; |
v.hwrite := '1'; |
v.haddr := r.rxaddr; |
v.mstate := st_rxtransfer; |
else |
-- Must fetch new RX descriptor. |
v_burstreq := '1'; |
v.hwrite := '0'; |
v.haddr := msti.rxdesc_ptr & "0"; |
v.mstate := st_rxgetdesc; |
end if; |
elsif r.txdma_act = '1' and msti.txdma_cancel = '0' and msti.txfifo_highw = '0' then |
-- Start TX transfer. |
if r.txdes_en = '1' then |
-- Transfer TX data from current descriptor. |
if unsigned(r.txdes_len) = 0 then |
-- Only send EOP/EEP and write back descriptor. |
v_burstreq := '1'; |
v.hwrite := '1'; |
v.haddr := msti.txdesc_ptr & "0"; |
v.txdesc_next := '1'; |
v.mstate := st_txputdesc; |
else |
-- Start burst transfer. |
v_burstreq := '1'; |
v.hwrite := '0'; |
v.haddr := r.txaddr; |
if unsigned(r.txdes_len) <= 4 then |
-- Transfer only one word. |
v.mstate := st_txfinal; |
else |
v.mstate := st_txtransfer; |
end if; |
end if; |
else |
-- Must fetch new TX descriptor. |
v_burstreq := '1'; |
v.hwrite := '0'; |
v.haddr := msti.txdesc_ptr & "0"; |
v.mstate := st_txgetdesc; |
end if; |
end if; |
|
when st_rxgetdesc => |
-- Read RX descriptor flags from memory. |
v_burstreq := '1'; |
v.hwrite := '0'; |
v.rxdes_len := ahbi.hrdata(15 downto 2); |
v.rxdes_en := ahbi.hrdata(16); |
v.rxdes_wr := ahbi.hrdata(17); |
v.rxdes_ie := ahbi.hrdata(18); |
v.rxdes_eop := '0'; |
v.rxdes_eep := '0'; |
v.rxdes_pos := (others => '0'); |
if v_burstack = '1' then |
-- Got descriptor flags. |
v_burstreq := '0'; |
v.mstate := st_rxgetptr; |
end if; |
|
when st_rxgetptr => |
-- Read RX data pointer from memory. |
v.rxaddr := ahbi.hrdata(31 downto 2); |
v.haddr := ahbi.hrdata(31 downto 2); |
v.firstword := '1'; |
if v_burstack = '1' then |
-- Got data pointer. |
if r.rxdes_en = '1' then |
-- Start transfer. |
v_burstreq := '1'; |
v.hwrite := '1'; |
v.mstate := st_rxtransfer; |
else |
-- Reached end of valid descriptors; stop. |
v.rxdma_act := '0'; |
v.mstate := st_idle; |
end if; |
end if; |
|
when st_rxtransfer => |
-- Continue an RX transfer. |
v_burstreq := '1'; |
v.hwrite := '1'; |
v.firstword := '0'; |
if v_burstack = '1' or r.firstword = '1' then |
-- Setup first/next data word. |
v.hwdata := msti.rxfifo_rdata(31 downto 0); |
v_rxfifo_read := '1'; |
-- Update pointers. |
v.rxdes_len := std_logic_vector(unsigned(r.rxdes_len) - 1); |
v.rxdes_pos := std_logic_vector(unsigned(r.rxdes_pos) + 4); |
v.rxaddr := std_logic_vector(unsigned(r.rxaddr) + 1); |
-- Detect EOP/EEP. |
v.rxdes_eop := |
(msti.rxfifo_rdata(35) and not msti.rxfifo_rdata(24)) or |
(msti.rxfifo_rdata(34) and not msti.rxfifo_rdata(16)) or |
(msti.rxfifo_rdata(33) and not msti.rxfifo_rdata(8)) or |
(msti.rxfifo_rdata(32) and not msti.rxfifo_rdata(0)); |
v.rxdes_eep := |
(msti.rxfifo_rdata(35) and msti.rxfifo_rdata(24)) or |
(msti.rxfifo_rdata(34) and msti.rxfifo_rdata(16)) or |
(msti.rxfifo_rdata(33) and msti.rxfifo_rdata(8)) or |
(msti.rxfifo_rdata(32) and msti.rxfifo_rdata(0)); |
-- Adjust frame length in case of EOP/EEP. |
if msti.rxfifo_rdata(35) = '1' then |
v.rxdes_pos := r.rxdes_pos(r.rxdes_pos'high downto 2) & "00"; |
elsif msti.rxfifo_rdata(34) = '1' then |
v.rxdes_pos := r.rxdes_pos(r.rxdes_pos'high downto 2) & "01"; |
elsif msti.rxfifo_rdata(33) = '1' then |
v.rxdes_pos := r.rxdes_pos(r.rxdes_pos'high downto 2) & "10"; |
elsif msti.rxfifo_rdata(32) = '1' then |
v.rxdes_pos := r.rxdes_pos(r.rxdes_pos'high downto 2) & "11"; |
end if; |
-- Stop at end of requested length or end of packet or fifo empty. |
if msti.rxfifo_nxempty = '1' or |
orv(msti.rxfifo_rdata(35 downto 32)) = '1' or |
unsigned(r.rxdes_len) = 1 then |
v_burstreq := '0'; |
v.mstate := st_rxfinal; |
end if; |
-- Stop at max burst length boundary. |
if (andv(r.rxaddr(maxburst+2 downto 2)) = '1') then |
v_burstreq := '0'; |
v.mstate := st_rxfinal; |
end if; |
end if; |
|
when st_rxfinal => |
-- Last data cycle of an RX transfer. |
if v_burstack = '1' then |
if unsigned(r.rxdes_len) = 0 or |
r.rxdes_eop = '1' or r.rxdes_eep = '1' then |
-- End of frame; write back descriptor. |
v_burstreq := '1'; |
v.hwrite := '1'; |
v.haddr := msti.rxdesc_ptr & "0"; |
v.rxdesc_next := '1'; |
v.mstate := st_rxputdesc; |
else |
-- Go through st_idle to pick up more work. |
v.mstate := st_idle; |
end if; |
end if; |
-- Give preference to TX work since we just did some RX work. |
v.prefertx := '1'; |
|
when st_rxputdesc => |
-- Write back RX descriptor. |
v.hwdata(15 downto 0) := r.rxdes_pos; |
v.hwdata(16) := '0'; |
v.hwdata(17) := r.rxdes_wr; |
v.hwdata(18) := r.rxdes_ie; |
v.hwdata(19) := '1'; |
v.hwdata(20) := r.rxdes_eop; |
v.hwdata(21) := r.rxdes_eep; |
v.hwdata(31 downto 22) := (others => '0'); |
if v_burstack = '1' then |
-- Frame done. |
v.rxdes_en := '0'; |
v.int_rxdesc := r.rxdes_ie; |
v.int_rxpacket := r.rxdes_eop or r.rxdes_eep; |
-- Go to st_idle. |
v.mstate := st_idle; |
end if; |
|
when st_txgetdesc => |
-- Read TX descriptor flags from memory. |
v_burstreq := '1'; |
v.hwrite := '0'; |
v.txdes_len := ahbi.hrdata(15 downto 0); |
v.txdes_en := ahbi.hrdata(16); |
v.txdes_wr := ahbi.hrdata(17); |
v.txdes_ie := ahbi.hrdata(18); |
v.txdes_eop := ahbi.hrdata(20); |
v.txdes_eep := ahbi.hrdata(21); |
if v_burstack = '1' then |
-- Got descriptor flags. |
v_burstreq := '0'; |
v.mstate := st_txgetptr; |
end if; |
|
when st_txgetptr => |
-- Read TX data pointer from memory. |
v.txaddr := ahbi.hrdata(31 downto 2); |
if v_burstack = '1' then |
-- Got data pointer. |
if r.txdes_en = '1' then |
-- Start transfer. |
if unsigned(r.txdes_len) = 0 then |
-- Only send EOP/EEP and write back descriptor. |
v_burstreq := '1'; |
v.hwrite := '1'; |
v.haddr := msti.txdesc_ptr & "0"; |
v.txdesc_next := '1'; |
v.mstate := st_txputdesc; |
else |
v_burstreq := '1'; |
v.hwrite := '0'; |
v.haddr := ahbi.hrdata(31 downto 2); |
if unsigned(r.txdes_len) <= 4 then |
-- Transfer only one word. |
v.mstate := st_txfinal; |
else |
v.mstate := st_txtransfer; |
end if; |
end if; |
else |
-- Reached end of valid descriptors; stop. |
v.txdma_act := '0'; |
v.mstate := st_idle; |
end if; |
end if; |
|
when st_txtransfer => |
-- Continue an TX transfer. |
v_burstreq := '1'; |
v.hwrite := '0'; |
if v_burstack = '1' then |
-- Got next data word from memory. |
v_txfifo_write := '1'; |
-- Update pointers. |
v.txdes_len := std_logic_vector(unsigned(r.txdes_len) - 4); |
v.txaddr := std_logic_vector(unsigned(r.txaddr) + 1); |
-- Handle end of burst/transfer. |
if andv(r.txaddr(maxburst+2 downto 2)) = '1' then |
-- This was the last data cycle before the max burst boundary. |
-- Go through st_idle to pick up more work. |
v_burstreq := '0'; |
v.mstate := st_idle; |
elsif msti.txfifo_nxfull = '1' then |
-- Fifo full; stop transfer, ignore final data cycle. |
v_burstreq := '0'; |
v.mstate := st_txskip; |
elsif unsigned(r.txdes_len) <= 8 then |
-- Stop at end of requested length (one more data cycle). |
v_burstreq := '0'; |
v.mstate := st_txfinal; |
elsif andv(r.txaddr(maxburst+2 downto 3)) = '1' then |
-- Stop at max burst length boundary (one more data cycle). |
v_burstreq := '0'; |
end if; |
else |
if andv(r.txaddr(maxburst+2 downto 2)) = '1' then |
-- Stop at max burst length boundary (just one more data cycle). |
v_burstreq := '0'; |
end if; |
end if; |
|
when st_txfinal => |
-- Last data cycle of a TX descriptor (1 <= txdes_len <= 4). |
if v_burstack = '1' then |
-- Got last data word from memory. |
v_txfifo_write := '1'; |
v.txdes_len := std_logic_vector(unsigned(r.txdes_len) - 4); |
-- Insert EOP in last word if needed. |
-- (Or set bit 7 in the flag byte to indicate that the |
-- frame ends while the packet continues.) |
case r.txdes_len(1 downto 0) is |
when "01" => |
v_txfifo_wdata(34) := '1'; |
v_txfifo_wdata(23) := not (r.txdes_eop or r.txdes_eep); |
v_txfifo_wdata(22 downto 17) := "000000"; |
v_txfifo_wdata(16) := r.txdes_eep; |
when "10" => |
v_txfifo_wdata(33) := '1'; |
v_txfifo_wdata(15) := not (r.txdes_eop or r.txdes_eep); |
v_txfifo_wdata(14 downto 9) := "000000"; |
v_txfifo_wdata(8) := r.txdes_eep; |
when "11" => |
v_txfifo_wdata(32) := '1'; |
v_txfifo_wdata(7) := not (r.txdes_eop or r.txdes_eep); |
v_txfifo_wdata(6 downto 1) := "000000"; |
v_txfifo_wdata(0) := r.txdes_eep; |
when others => |
-- txdes_len = 4 |
-- Store 4 data bytes now; store EOP in st_txputdesc (if needed). |
end case; |
if msti.txfifo_nxfull = '1' and r.txdes_len(1 downto 0) = "00" then |
-- Fifo full so no room to store EOP. |
v.mstate := st_idle; |
v.haddr := msti.txdesc_ptr & "0"; |
else |
-- Prepare to write back descriptor. |
v_burstreq := '1'; |
v.hwrite := '1'; |
v.haddr := msti.txdesc_ptr & "0"; |
v.txdesc_next := '1'; |
v.mstate := st_txputdesc; |
end if; |
end if; |
|
when st_txputdesc => |
-- Write back TX descriptor. |
v.hwdata(15 downto 0) := (others => '0'); |
v.hwdata(16) := '0'; |
v.hwdata(17) := r.txdes_wr; |
v.hwdata(18) := r.txdes_ie; |
v.hwdata(19) := '1'; |
v.hwdata(20) := r.txdes_eop; |
v.hwdata(21) := r.txdes_eep; |
v.hwdata(31 downto 22) := (others => '0'); |
if v_burstack = '1' then |
if r.txdes_len(1 downto 0) = "00" and |
(r.txdes_eop = '1' or r.txdes_eep = '1') then |
-- Store EOP in TX fifo. |
v_txfifo_write := '1'; |
v_txfifo_wdata(35) := '1'; |
v_txfifo_wdata(31 downto 25) := "0000000"; |
v_txfifo_wdata(24) := r.txdes_eep; |
end if; |
-- Frame done. |
v.txdes_en := '0'; |
v.int_txdesc := r.txdes_ie; |
-- Go to st_idle and give preference to RX work. |
v.mstate := st_idle; |
end if; |
|
when st_txskip => |
-- Ignore last data cycle of burst because TX fifo is full. |
if v_burstack = '1' then |
v.mstate := st_idle; |
end if; |
|
end case; |
|
-- Abort DMA when an AHB error occurs. |
if r.ahberror = '1' then |
v.rxdma_act := '0'; |
v.txdma_act := '0'; |
v.mstate := st_idle; |
end if; |
|
|
-- |
-- Burst state machine. |
-- |
-- A transfer starts when the main state machine combinatorially pulls |
-- v_burstreq high and assigns v.haddr and v.hwrite (i.e. r.haddr and |
-- r.hwrite must be valid in the first clock cycle AFTER rising v_burstreq). |
-- In case of a write transfer, r.hwdata must be valid in the second |
-- clock cycle after rising v_burstreq. |
-- |
-- During the transfer, the burst state machine announces each word |
-- with a v_burstack pulse. During a read transfer, ahbi.hrdata is |
-- valid when v_burstack is high. During a write transfer, a next |
-- word must be assigned to v.hwdata on the v_burstack pulse. |
-- |
-- For a single-word transfer, v_burstreq should be high for only one |
-- clock cycle. For a multi-word transfer, v_burstreq should be high |
-- until the last-but-one v_burstack pulse. I.e. after v_burstreq is |
-- released combinatorially on a v_burstack pulse, one last v_burstack |
-- pulse will follow. |
-- |
-- The burst state machine transparently handles bus arbitration and |
-- retrying of transfers. In case of a non-retryable error, r.ahberror |
-- is set high and further transfers are blocked. The main state |
-- machine is responsible for ensuring that bursts do not cross a |
-- forbidden address boundary. |
-- |
case r.burststat is |
|
when bs_idle => |
-- Wait for request and bus grant. |
-- (htrans = HTRANS_IDLE) |
v.hbusreq := r.hbusreq or v_burstreq; |
if (r.hbusreq = '1' or v_burstreq = '1') and |
ahbi.hready = '1' and |
ahbi.hgrant(hindex) = '1' then |
-- Start burst. |
v.burststat := bs_setup; |
end if; |
-- Block new bursts after an error occurred. |
if r.ahberror = '1' then |
v.hbusreq := '0'; |
v.burststat := bs_idle; |
end if; |
|
when bs_setup => |
-- First address cycle. |
-- (htrans = HTRANS_NONSEQ) |
v.hbusreq := '1'; |
if ahbi.hready = '1' then |
-- Increment address and continue burst in bs_active. |
v.haddr := std_logic_vector(unsigned(r.haddr) + 1); |
v.burststat := bs_active; |
-- Stop burst when application ends the transfer. |
v.hbusreq := v_burstreq; |
if v_burstreq = '0' then |
v.burststat := bs_end; |
end if; |
-- Stop burst when we are kicked off the bus. |
if ahbi.hgrant(hindex) = '0' then |
v.burststat := bs_end; |
end if; |
end if; |
|
when bs_active => |
-- Continue burst. |
-- (htrans = HTRANS_SEQ) |
v.hbusreq := '1'; |
if ahbi.hresp /= HRESP_OKAY then |
-- Error response from slave. |
v.haddr := std_logic_vector(unsigned(r.haddr) - 1); |
if ahbi.hresp = HRESP_ERROR then |
-- Permanent error. |
v.ahberror := '1'; |
v.hbusreq := '0'; |
else |
-- Must retry request. |
v.hbusreq := '1'; |
end if; |
v.burststat := bs_idle; |
elsif ahbi.hready = '1' then |
-- Increment address. |
v.haddr := std_logic_vector(unsigned(r.haddr) + 1); |
-- Stop burst when application ends the transfer. |
v.hbusreq := v_burstreq; |
if v_burstreq = '0' then |
v.burststat := bs_end; |
end if; |
-- Stop burst when we are kicked off the bus. |
if ahbi.hgrant(hindex) = '0' then |
v.burststat := bs_end; |
end if; |
end if; |
|
when bs_end => |
-- Last data cycle of burst. |
-- (htrans = HTRANS_IDLE) |
v.hbusreq := r.hbusreq or v_burstreq; |
if ahbi.hresp /= HRESP_OKAY then |
-- Error response from slave. |
v.haddr := std_logic_vector(unsigned(r.haddr) - 1); |
if ahbi.hresp = HRESP_ERROR then |
-- Permanent error. |
v.ahberror := '1'; |
v.hbusreq := '0'; |
else |
-- Must retry request. |
v.hbusreq := '1'; |
end if; |
v.burststat := bs_idle; |
elsif ahbi.hready = '1' then |
-- Burst complete. |
if (r.hbusreq = '1' or v_burstreq = '1') and |
ahbi.hgrant(hindex) = '1' then |
-- Immediately start next burst. |
v.burststat := bs_setup; |
else |
v.burststat := bs_idle; |
end if; |
end if; |
|
end case; |
|
|
-- |
-- Drive output signals. |
-- |
ahbo.hbusreq <= r.hbusreq; |
if r.burststat = bs_setup then |
ahbo.htrans <= HTRANS_NONSEQ; |
elsif r.burststat = bs_active then |
ahbo.htrans <= HTRANS_SEQ; |
else |
ahbo.htrans <= HTRANS_IDLE; |
end if; |
ahbo.haddr <= r.haddr & "00"; |
ahbo.hwrite <= r.hwrite; |
ahbo.hwdata <= r.hwdata; |
ahbo.hlock <= '0'; -- never lock the bus |
ahbo.hsize <= HSIZE_DWORD; -- always 32-bit words |
ahbo.hburst <= HBURST_INCR; -- undetermined incremental burst |
ahbo.hprot <= "0011"; -- not cacheable, privileged, data |
ahbo.hirq <= (others => '0'); -- no interrupts via AHB bus |
ahbo.hconfig <= hconfig; -- AHB plug&play data |
ahbo.hindex <= hindex; -- index feedback |
|
msto.rxdma_act <= r.rxdma_act; |
msto.txdma_act <= r.txdma_act; |
msto.ahberror <= r.ahberror; |
msto.int_rxdesc <= r.int_rxdesc; |
msto.int_txdesc <= r.int_txdesc; |
msto.int_rxpacket <= r.int_rxpacket; |
msto.rxdesc_next <= r.rxdesc_next; |
msto.rxdesc_wrap <= r.rxdesc_next and r.rxdes_wr; |
msto.txdesc_next <= r.txdesc_next; |
msto.txdesc_wrap <= r.txdesc_next and r.txdes_wr; |
msto.rxfifo_read <= v_rxfifo_read; |
msto.txfifo_write <= v_txfifo_write; |
msto.txfifo_wdata <= v_txfifo_wdata; |
|
|
-- |
-- Reset. |
-- |
if rstn = '0' then |
v := regs_reset; |
end if; |
|
|
-- |
-- Update registers. |
-- |
rin <= v; |
end process; |
|
|
-- |
-- Synchronous process: update registers. |
-- |
process (clk) is |
begin |
if rising_edge(clk) then |
r <= rin; |
end if; |
end process; |
|
end architecture spwahbmst_arch; |
/spwamba.vhd
0,0 → 1,864
-- |
-- SpaceWire core with AMBA interface. |
-- |
-- APB registers: |
-- |
-- Address 0x00: Control Register |
-- bit 0 Reset spwamba core (auto-clear) |
-- bit 1 Reset DMA engines (auto-clear) |
-- bit 2 Link start |
-- bit 3 Link autostart |
-- bit 4 Link disable |
-- bit 5 Enable timecode transmission through tick_in signal |
-- bit 6 Start RX DMA (auto-clear) |
-- bit 7 Start TX DMA (auto-clear) |
-- bit 8 Cancel TX DMA and discard TX data queue (auto-clear) |
-- bit 9 Enable interrupt on link up/down |
-- bit 10 Enable interrupt on time code received |
-- bit 11 Enable interrupt on RX descriptor |
-- bit 12 Enable interrupt on TX descriptor |
-- bit 13 Enable interrupt on RX packet |
-- bit 27:24 desctablesize (read-only) |
-- |
-- Address 0x04: Status Register |
-- bit 1:0 Link status: 0=off, 1=started, 2=connecting, 3=run |
-- bit 2 Got disconnect error (sticky) |
-- bit 3 Got parity error (sticky) |
-- bit 4 Got escape error (sticky) |
-- bit 5 Got credit error (sticky) |
-- bit 6 RX DMA enabled |
-- bit 7 TX DMA enabled |
-- bit 8 AHB error occurred (reset DMA engine to clear) |
-- bit 9 Reserved |
-- bit 10 Received timecode (sticky) |
-- bit 11 Finished RX descriptor with IE='1' (sticky) |
-- bit 12 Finished TX descriptor with IE='1' (sticky) |
-- bit 13 Received packet (sticky) |
-- bit 14 RX buffer empty after packet |
-- |
-- Sticky bits are reset by writing a '1' bit to the corresponding |
-- bit position(s). |
-- |
-- Address 0x08: Transmission Clock Scaler |
-- bit 7:0 txclk division factor minus 1 |
-- |
-- Address 0x0c: Timecode Register |
-- bit 5:0 Last received timecode value (read-only) |
-- bit 7:6 Control bits received with last timecode (read-only) |
-- bit 13:8 Timecode value to send on next tick_in (auto-increment) |
-- bit 15:14 Reserved (write as zero) |
-- bit 16 Write '1' to send a timecode (auto-clear) |
-- |
-- Address 0x10: Descriptor pointer for RX DMA |
-- bit 2:0 Reserved, write as zero |
-- bit desctablesize+2:3 Descriptor index (auto-increment) |
-- bit 31:desctablesize+3 Fixed address bits of descriptor table |
-- |
-- For example, if desctablesize = 10, a 8192-byte area is |
-- determined by bits 31:13. This area has room for 1024 descriptors |
-- of 8 bytes each. Bits 12:3 point to the current descriptor within |
-- the table. |
-- |
-- Address 0x14: Descriptor pointer for TX DMA |
-- bit 2:0 Reserved, write as zero |
-- bit desctablesize+2:3 Descriptor index (auto-increment) |
-- bit 31:desctablesize+3 Fixed address bits of descriptor table |
-- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
library techmap; |
use techmap.gencomp.all; |
library grlib; |
use grlib.amba.all; |
use grlib.devices.all; |
use grlib.stdlib.all; |
use work.spwpkg.all; |
use work.spwambapkg.all; |
|
entity spwamba is |
|
generic ( |
-- Technology selection for FIFO memories. |
tech: integer range 0 to NTECH := DEFFABTECH; |
|
-- AHB master index. |
hindex: integer; |
|
-- APB slave index. |
pindex: integer; |
|
-- Bits 19 to 8 of the APB address range. |
paddr: integer; |
|
-- Mask for APB address bits 19 to 8. |
pmask: integer := 16#fff#; |
|
-- Index of the interrupt request line. |
pirq: integer; |
|
-- System clock frequency in Hz. |
-- This must be set to the frequency of "clk". It is used to setup |
-- counters for reset timing, disconnect timeout and to transmit |
-- at 10 Mbit/s during the link handshake. |
sysfreq: real; |
|
-- Transmit clock frequency in Hz (only if tximpl = impl_fast). |
-- This must be set to the frequency of "txclk". It is used to |
-- transmit at 10 Mbit/s during the link handshake. |
txclkfreq: real := 0.0; |
|
-- Selection of a receiver front-end implementation. |
rximpl: spw_implementation_type := impl_generic; |
|
-- Maximum number of bits received per system clock |
-- (must be 1 in case of impl_generic). |
rxchunk: integer range 1 to 4 := 1; |
|
-- Selection of a transmitter implementation. |
tximpl: spw_implementation_type := impl_generic; |
|
-- Enable capability to generate time-codes. |
timecodegen: boolean := true; |
|
-- Size of the receive FIFO as the 2-logarithm of the number of words. |
-- Must be at least 6 (64 words = 256 bytes). |
rxfifosize: integer range 6 to 12 := 8; |
|
-- Size of the transmit FIFO as the 2-logarithm of the number of words. |
txfifosize: integer range 2 to 12 := 8; |
|
-- Size of the DMA descriptor tables as the 2-logarithm of the number |
-- of descriptors. |
desctablesize: integer range 4 to 14 := 10; |
|
-- Maximum burst length as the 2-logarithm of the number of words (default 8 words). |
maxburst: integer range 1 to 8 := 3 |
); |
|
port ( |
-- System clock. |
clk: in std_logic; |
|
-- Receiver sample clock (only for impl_fast) |
rxclk: in std_logic; |
|
-- Transmit clock (only for impl_fast) |
txclk: in std_logic; |
|
-- Synchronous reset (active-low). |
rstn: in std_logic; |
|
-- APB slave input signals. |
apbi: in apb_slv_in_type; |
|
-- APB slave output signals. |
apbo: out apb_slv_out_type; |
|
-- AHB master input signals. |
ahbi: in ahb_mst_in_type; |
|
-- AHB master output signals. |
ahbo: out ahb_mst_out_type; |
|
-- Pulse for TimeCode generation. |
tick_in: in std_logic; |
|
-- Data In signal from SpaceWire bus. |
spw_di: in std_logic; |
|
-- Strobe In signal from SpaceWire bus. |
spw_si: in std_logic; |
|
-- Data Out signal to SpaceWire bus. |
spw_do: out std_logic; |
|
-- Strobe Out signal to SpaceWire bus. |
spw_so: out std_logic |
); |
|
end entity spwamba; |
|
architecture spwamba_arch of spwamba is |
|
-- Reset time (6.4 us) in system clocks |
constant reset_time: integer := integer(sysfreq * 6.4e-6); |
|
-- Disconnect time (850 ns) in system clocks |
constant disconnect_time: integer := integer(sysfreq * 850.0e-9); |
|
-- Initial tx clock scaler (10 Mbit). |
type impl_to_real_type is array(spw_implementation_type) of real; |
constant tximpl_to_txclk_freq: impl_to_real_type := |
(impl_generic => sysfreq, impl_fast => txclkfreq); |
constant effective_txclk_freq: real := tximpl_to_txclk_freq(tximpl); |
constant default_divcnt: std_logic_vector(7 downto 0) := |
std_logic_vector(to_unsigned(integer(effective_txclk_freq / 10.0e6 - 1.0), 8)); |
|
-- Registers. |
type regs_type is record |
-- packet state |
rxpacket: std_logic; -- '1' when receiving a packet |
rxeep: std_logic; -- '1' when rx EEP character pending |
txpacket: std_logic; -- '1' when transmitting a packet |
txdiscard: std_logic; -- '1' when discarding a tx packet |
-- RX fifo state |
rxfifo_raddr: std_logic_vector(rxfifosize-1 downto 0); |
rxfifo_waddr: std_logic_vector(rxfifosize-1 downto 0); |
rxfifo_wdata: std_logic_vector(35 downto 0); |
rxfifo_write: std_ulogic; |
rxfifo_empty: std_ulogic; |
rxfifo_bytemsk: std_logic_vector(2 downto 0); |
rxroom: std_logic_vector(5 downto 0); |
-- TX fifo state |
txfifo_raddr: std_logic_vector(txfifosize-1 downto 0); |
txfifo_waddr: std_logic_vector(txfifosize-1 downto 0); |
txfifo_empty: std_ulogic; |
txfifo_nxfull: std_ulogic; |
txfifo_highw: std_ulogic; |
txfifo_bytepos: std_logic_vector(1 downto 0); |
-- APB registers |
ctl_reset: std_ulogic; |
ctl_resetdma: std_ulogic; |
ctl_linkstart: std_ulogic; |
ctl_autostart: std_ulogic; |
ctl_linkdis: std_ulogic; |
ctl_ticken: std_ulogic; |
ctl_rxstart: std_ulogic; |
ctl_txstart: std_ulogic; |
ctl_txcancel: std_ulogic; |
ctl_ielink: std_ulogic; |
ctl_ietick: std_ulogic; |
ctl_ierxdesc: std_ulogic; |
ctl_ietxdesc: std_ulogic; |
ctl_ierxpacket: std_ulogic; |
sta_link: std_logic_vector(1 downto 0); |
sta_errdisc: std_ulogic; |
sta_errpar: std_ulogic; |
sta_erresc: std_ulogic; |
sta_errcred: std_ulogic; |
sta_gottick: std_ulogic; |
sta_rxdesc: std_ulogic; |
sta_txdesc: std_ulogic; |
sta_rxpacket: std_ulogic; |
sta_rxempty: std_ulogic; |
txdivcnt: std_logic_vector(7 downto 0); |
time_in: std_logic_vector(5 downto 0); |
tick_in: std_ulogic; |
rxdesc_ptr: std_logic_vector(31 downto 3); |
txdesc_ptr: std_logic_vector(31 downto 3); |
-- APB interrupt request |
irq: std_ulogic; |
end record; |
|
constant regs_reset: regs_type := ( |
rxpacket => '0', |
rxeep => '0', |
txpacket => '0', |
txdiscard => '0', |
rxfifo_raddr => (others => '0'), |
rxfifo_waddr => (others => '0'), |
rxfifo_wdata => (others => '0'), |
rxfifo_write => '0', |
rxfifo_empty => '1', |
rxfifo_bytemsk => "111", |
rxroom => (others => '1'), |
txfifo_raddr => (others => '0'), |
txfifo_waddr => (others => '0'), |
txfifo_empty => '1', |
txfifo_nxfull => '0', |
txfifo_highw => '0', |
txfifo_bytepos => "00", |
ctl_reset => '0', |
ctl_resetdma => '0', |
ctl_linkstart => '0', |
ctl_autostart => '0', |
ctl_linkdis => '0', |
ctl_ticken => '0', |
ctl_rxstart => '0', |
ctl_txstart => '0', |
ctl_txcancel => '0', |
ctl_ielink => '0', |
ctl_ietick => '0', |
ctl_ierxdesc => '0', |
ctl_ietxdesc => '0', |
ctl_ierxpacket => '0', |
sta_link => "00", |
sta_errdisc => '0', |
sta_errpar => '0', |
sta_erresc => '0', |
sta_errcred => '0', |
sta_gottick => '0', |
sta_rxdesc => '0', |
sta_txdesc => '0', |
sta_rxpacket => '0', |
sta_rxempty => '1', |
txdivcnt => default_divcnt, |
time_in => (others => '0'), |
tick_in => '0', |
rxdesc_ptr => (others => '0'), |
txdesc_ptr => (others => '0'), |
irq => '0' ); |
|
signal r: regs_type := regs_reset; |
signal rin: regs_type; |
|
-- Component interface signals. |
signal recv_rxen: std_logic; |
signal recvo: spw_recv_out_type; |
signal recv_inact: std_logic; |
signal recv_inbvalid: std_logic; |
signal recv_inbits: std_logic_vector(rxchunk-1 downto 0); |
signal xmiti: spw_xmit_in_type; |
signal xmito: spw_xmit_out_type; |
signal xmit_divcnt: std_logic_vector(7 downto 0); |
signal linki: spw_link_in_type; |
signal linko: spw_link_out_type; |
signal msti: spw_ahbmst_in_type; |
signal msto: spw_ahbmst_out_type; |
signal ahbmst_rstn: std_logic; |
signal s_rst: std_logic; |
|
-- Memory interface signals. |
signal s_rxfifo_raddr: std_logic_vector(rxfifosize-1 downto 0); |
signal s_rxfifo_rdata: std_logic_vector(35 downto 0); |
signal s_rxfifo_wen: std_logic; |
signal s_rxfifo_waddr: std_logic_vector(rxfifosize-1 downto 0); |
signal s_rxfifo_wdata: std_logic_vector(35 downto 0); |
signal s_txfifo_raddr: std_logic_vector(txfifosize-1 downto 0); |
signal s_txfifo_rdata: std_logic_vector(35 downto 0); |
signal s_txfifo_wen: std_logic; |
signal s_txfifo_waddr: std_logic_vector(txfifosize-1 downto 0); |
signal s_txfifo_wdata: std_logic_vector(35 downto 0); |
|
|
-- APB slave plug&play configuration |
constant REVISION: integer := 0; |
constant pconfig: apb_config_type := ( |
0 => ahb_device_reg(VENDOR_OPENCORES, DEVICE_SPACEWIRELIGHT, 0, REVISION, pirq), |
1 => apb_iobar(paddr, pmask) ); |
|
-- AHB master plug&play configuration |
constant hconfig: ahb_config_type := ( |
0 => ahb_device_reg(VENDOR_OPENCORES, DEVICE_SPACEWIRELIGHT, 0, REVISION, 0), |
others => zero32 ); |
|
begin |
|
-- Instantiate link controller. |
link_inst: spwlink |
generic map ( |
reset_time => reset_time ) |
port map ( |
clk => clk, |
rst => s_rst, |
linki => linki, |
linko => linko, |
rxen => recv_rxen, |
recvo => recvo, |
xmiti => xmiti, |
xmito => xmito ); |
|
-- Instantiate receiver. |
recv_inst: spwrecv |
generic map( |
disconnect_time => disconnect_time, |
rxchunk => rxchunk ) |
port map ( |
clk => clk, |
rxen => recv_rxen, |
recvo => recvo, |
inact => recv_inact, |
inbvalid => recv_inbvalid, |
inbits => recv_inbits ); |
|
-- Instantiate receiver front-end. |
recvfront_sel0: if rximpl = impl_generic generate |
recvfront_generic_inst: spwrecvfront_generic |
port map ( |
clk => clk, |
rxen => recv_rxen, |
inact => recv_inact, |
inbvalid => recv_inbvalid, |
inbits => recv_inbits, |
spw_di => spw_di, |
spw_si => spw_si ); |
end generate; |
recvfront_sel1: if rximpl = impl_fast generate |
recvfront_fast_inst: spwrecvfront_fast |
generic map ( |
rxchunk => rxchunk ) |
port map ( |
clk => clk, |
rxclk => rxclk, |
rxen => recv_rxen, |
inact => recv_inact, |
inbvalid => recv_inbvalid, |
inbits => recv_inbits, |
spw_di => spw_di, |
spw_si => spw_si ); |
end generate; |
|
-- Instantiate transmitter. |
xmit_sel0: if tximpl = impl_generic generate |
xmit_inst: spwxmit |
port map ( |
clk => clk, |
rst => s_rst, |
divcnt => xmit_divcnt, |
xmiti => xmiti, |
xmito => xmito, |
spw_do => spw_do, |
spw_so => spw_so ); |
end generate; |
xmit_sel1: if tximpl = impl_fast generate |
xmit_fast_inst: spwxmit_fast |
port map ( |
clk => clk, |
txclk => txclk, |
rst => s_rst, |
divcnt => xmit_divcnt, |
xmiti => xmiti, |
xmito => xmito, |
spw_do => spw_do, |
spw_so => spw_so ); |
end generate; |
|
-- Instantiate RX FIFO. |
rxfifo: syncram_2p |
generic map ( |
tech => tech, |
abits => rxfifosize, |
dbits => 36, |
sepclk => 0 ) |
port map ( |
rclk => clk, |
renable => '1', |
raddress => s_rxfifo_raddr, |
dataout => s_rxfifo_rdata, |
wclk => clk, |
write => s_rxfifo_wen, |
waddress => s_rxfifo_waddr, |
datain => s_rxfifo_wdata ); |
|
-- Instantiate TX FIFO. |
txfifo: syncram_2p |
generic map ( |
tech => tech, |
abits => txfifosize, |
dbits => 36, |
sepclk => 0 ) |
port map ( |
rclk => clk, |
renable => '1', |
raddress => s_txfifo_raddr, |
dataout => s_txfifo_rdata, |
wclk => clk, |
write => s_txfifo_wen, |
waddress => s_txfifo_waddr, |
datain => s_txfifo_wdata ); |
|
-- Instantiate AHB master. |
ahbmst: spwahbmst |
generic map ( |
hindex => hindex, |
hconfig => hconfig, |
maxburst => maxburst ) |
port map ( |
clk => clk, |
rstn => ahbmst_rstn, |
msti => msti, |
msto => msto, |
ahbi => ahbi, |
ahbo => ahbo ); |
|
|
-- |
-- Combinatorial process |
-- |
process (r, linko, msto, s_rxfifo_rdata, s_txfifo_rdata, rstn, apbi, tick_in) is |
variable v: regs_type; |
variable v_tmprxroom: unsigned(rxfifosize-1 downto 0); |
variable v_prdata: std_logic_vector(31 downto 0); |
variable v_txfifo_bytepos: integer range 0 to 3; |
begin |
v := r; |
v_tmprxroom := to_unsigned(0, rxfifosize); |
v_prdata := (others => '0'); |
|
-- Convert RX/TX byte index to integer. |
v_txfifo_bytepos := to_integer(unsigned(r.txfifo_bytepos)); |
|
-- Reset auto-clearing registers. |
v.ctl_reset := '0'; |
v.ctl_resetdma := '0'; |
v.ctl_rxstart := '0'; |
v.ctl_txstart := '0'; |
|
-- Register external timecode trigger (if enabled). |
if timecodegen and r.ctl_ticken = '1' then |
v.tick_in := tick_in; |
else |
v.tick_in := '0'; |
end if; |
|
-- Auto-increment timecode counter. |
if r.tick_in = '1' then |
v.time_in := std_logic_vector(unsigned(r.time_in) + 1); |
end if; |
|
-- Keep track of whether we are sending and/or receiving a packet. |
if linko.rxchar = '1' then |
-- got character |
v.rxpacket := not linko.rxflag; |
end if; |
if linko.txack = '1' then |
-- send character |
v.txpacket := not s_txfifo_rdata(35-v_txfifo_bytepos); |
end if; |
|
-- Accumulate a word to write to the RX fifo. |
-- Note: If the EOP/EEP marker falls in the middle of a word, |
-- subsequent bytes must be a copy of the marker, otherwise |
-- the AHB master may not work correctly. |
v.rxfifo_write := '0'; |
for i in 3 downto 0 loop |
if (i = 0) or (r.rxfifo_bytemsk(i-1) = '1') then |
if r.rxeep = '1' then |
v.rxfifo_wdata(32+i) := '1'; |
v.rxfifo_wdata(7+8*i downto 8*i) := "00000001"; |
else |
v.rxfifo_wdata(32+i) := linko.rxflag; |
v.rxfifo_wdata(7+8*i downto 8*i) := linko.rxdata; |
end if; |
end if; |
end loop; |
if linko.rxchar = '1' or (r.rxeep = '1' and unsigned(r.rxroom) /= 0) then |
v.rxeep := '0'; |
if r.rxfifo_bytemsk(0) = '0' or linko.rxflag = '1' or r.rxeep = '1' then |
-- Flush the current word to the FIFO. |
v.rxfifo_write := '1'; |
v.rxfifo_bytemsk := "111"; |
else |
-- Store one byte. |
v.rxfifo_bytemsk := '0' & r.rxfifo_bytemsk(2 downto 1); |
end if; |
end if; |
|
-- Read from TX fifo. |
if (r.txfifo_empty = '0') and (linko.txack = '1' or r.txdiscard = '1') then |
-- Update byte pointer. |
if r.txfifo_bytepos = "11" or |
s_txfifo_rdata(35-v_txfifo_bytepos) = '1' or |
(v_txfifo_bytepos < 3 and |
s_txfifo_rdata(34-v_txfifo_bytepos) = '1' and |
s_txfifo_rdata(23-8*v_txfifo_bytepos) = '1') then |
-- This is the last byte in the current word; |
-- OR the current byte is an EOP/EEP marker; |
-- OR the next byte in the current work is a non-EOP end-of-frame marker. |
v.txfifo_bytepos := "00"; |
v.txfifo_raddr := std_logic_vector(unsigned(r.txfifo_raddr) + 1); |
else |
-- Move to next byte. |
v.txfifo_bytepos := std_logic_vector(unsigned(r.txfifo_bytepos) + 1); |
end if; |
-- Clear discard flag when past EOP. |
if s_txfifo_rdata(35-v_txfifo_bytepos) = '1' then |
v.txdiscard := '0'; |
end if; |
end if; |
|
-- Update RX fifo pointers. |
if msto.rxfifo_read = '1' then |
-- Read one word. |
v.rxfifo_raddr := std_logic_vector(unsigned(r.rxfifo_raddr) + 1); |
end if; |
if r.rxfifo_write = '1' then |
-- Write one word. |
v.rxfifo_waddr := std_logic_vector(unsigned(r.rxfifo_waddr) + 1); |
end if; |
|
-- Detect RX fifo empty (using new value of rxfifo_raddr). |
-- Note: The FIFO is empty if head and tail pointer are equal. |
v.rxfifo_empty := conv_std_logic(v.rxfifo_raddr = r.rxfifo_waddr); |
|
-- Indicate RX fifo room for SpaceWire flow control. |
-- The flow control window is normally expressed as a number of bytes, |
-- but we don't know how many bytes we can fit in each word because |
-- some words are only partially used. So we report FIFO room as if |
-- each FIFO word can hold only one byte, which is an overly |
-- pessimistic estimate. |
-- (Use the new value of rxfifo_waddr.) |
v_tmprxroom := unsigned(r.rxfifo_raddr) - unsigned(v.rxfifo_waddr) - 1; |
if v_tmprxroom > 63 then |
-- at least 64 bytes room. |
v.rxroom := "111111"; |
else |
-- less than 64 bytes room. |
v.rxroom := std_logic_vector(v_tmprxroom(5 downto 0)); |
end if; |
|
-- Update TX fifo write pointer. |
if msto.txfifo_write = '1' then |
-- write one word. |
v.txfifo_waddr := std_logic_vector(unsigned(r.txfifo_waddr) + 1); |
end if; |
|
-- Detect TX fifo empty. |
-- Note: The FIFO may be either full or empty if head and tail pointer |
-- are equal, hence the additional test for txfifo_nxfull. |
v.txfifo_empty := conv_std_logic(v.txfifo_raddr = r.txfifo_waddr) and not r.txfifo_nxfull; |
|
-- Detect TX fifo full after one more write. |
if unsigned(r.txfifo_raddr) - unsigned(r.txfifo_waddr) = to_unsigned(2, txfifosize) then |
-- currently exactly 2 words left. |
v.txfifo_nxfull := msto.txfifo_write; |
end if; |
|
-- Detect TX fifo more than 3/4 full. |
if unsigned(r.txfifo_raddr) - unsigned(r.txfifo_waddr) = to_unsigned(2**(txfifosize-2), txfifosize) then |
-- currently exactly 3/4 full. |
v.txfifo_highw := msto.txfifo_write; |
end if; |
|
-- Update descriptor pointers. |
if msto.rxdesc_next = '1' then |
if msto.rxdesc_wrap = '1' then |
v.rxdesc_ptr(desctablesize+2 downto 3) := (others => '0'); |
else |
v.rxdesc_ptr(desctablesize+2 downto 3) := |
std_logic_vector(unsigned(r.rxdesc_ptr(desctablesize+2 downto 3)) + 1); |
end if; |
end if; |
if msto.txdesc_next = '1' then |
if msto.txdesc_wrap = '1' then |
v.txdesc_ptr(desctablesize+2 downto 3) := (others => '0'); |
else |
v.txdesc_ptr(desctablesize+2 downto 3) := |
std_logic_vector(unsigned(r.txdesc_ptr(desctablesize+2 downto 3)) + 1); |
end if; |
end if; |
|
-- If the link is lost, set a flag to discard the current packet. |
if linko.running = '0' then |
v.rxeep := v.rxeep or v.rxpacket; -- use new value of rxpacket |
v.txdiscard := v.txdiscard or v.txpacket; -- use new value of txpacket |
v.rxpacket := '0'; |
v.txpacket := '0'; |
end if; |
|
-- Clear the discard flag when the link is explicitly disabled. |
if r.ctl_linkdis = '1' then |
v.txdiscard := '0'; |
end if; |
|
-- Extend TX cancel command until TX DMA has stopped. |
if msto.txdma_act = '0' then |
v.ctl_txcancel := '0'; |
end if; |
|
-- Update status registers. |
v.sta_link(0) := linko.running or linko.started; |
v.sta_link(1) := linko.running or linko.connecting; |
if linko.errdisc = '1' then v.sta_errdisc := '1'; end if; |
if linko.errpar = '1' then v.sta_errpar := '1'; end if; |
if linko.erresc = '1' then v.sta_erresc := '1'; end if; |
if linko.errcred = '1' then v.sta_errcred := '1'; end if; |
if linko.tick_out = '1' then v.sta_gottick := '1'; end if; |
if msto.int_rxdesc = '1' then v.sta_rxdesc := '1'; end if; |
if msto.int_txdesc = '1' then v.sta_txdesc := '1'; end if; |
if msto.int_rxpacket = '1' then v.sta_rxpacket := '1'; end if; |
if msto.int_rxpacket = '1' and r.rxfifo_empty = '1' then |
v.sta_rxempty := '1'; |
elsif r.rxfifo_empty = '0' then |
v.sta_rxempty := '0'; |
end if; |
|
-- Generate interrupt requests. |
v.irq := |
(r.ctl_ielink and (linko.running xor (r.sta_link(0) and r.sta_link(1)))) or |
(r.ctl_ietick and linko.tick_out) or |
(r.ctl_ierxdesc and msto.int_rxdesc) or |
(r.ctl_ietxdesc and msto.int_txdesc) or |
(r.ctl_ierxpacket and msto.int_rxpacket); |
|
-- APB read access. |
if apbi.psel(pindex) = '1' then |
case apbi.paddr(4 downto 2) is |
when "000" => -- read control register |
v_prdata(0) := '0'; |
v_prdata(1) := '0'; |
v_prdata(2) := r.ctl_linkstart; |
v_prdata(3) := r.ctl_autostart; |
v_prdata(4) := r.ctl_linkdis; |
v_prdata(5) := r.ctl_ticken; |
v_prdata(6) := '0'; |
v_prdata(7) := '0'; |
v_prdata(8) := r.ctl_txcancel; |
v_prdata(9) := r.ctl_ielink; |
v_prdata(10) := r.ctl_ietick; |
v_prdata(11) := r.ctl_ierxdesc; |
v_prdata(12) := r.ctl_ietxdesc; |
v_prdata(13) := r.ctl_ierxpacket; |
v_prdata(27 downto 24) := std_logic_vector(to_unsigned(desctablesize, 4)); |
when "001" => -- read status register |
v_prdata(1 downto 0) := r.sta_link; |
v_prdata(2) := r.sta_errdisc; |
v_prdata(3) := r.sta_errpar; |
v_prdata(4) := r.sta_erresc; |
v_prdata(5) := r.sta_errcred; |
v_prdata(6) := msto.rxdma_act; |
v_prdata(7) := msto.txdma_act; |
v_prdata(8) := msto.ahberror; |
v_prdata(10) := r.sta_gottick; |
v_prdata(11) := r.sta_rxdesc; |
v_prdata(12) := r.sta_txdesc; |
v_prdata(13) := r.sta_rxpacket; |
v_prdata(14) := r.sta_rxempty; |
when "010" => -- read transmission clock scaler |
v_prdata(7 downto 0) := r.txdivcnt; |
when "011" => -- read timecode register |
v_prdata(5 downto 0) := linko.time_out; |
v_prdata(7 downto 6) := linko.ctrl_out; |
v_prdata(13 downto 8) := r.time_in; |
v_prdata(16 downto 14) := "000"; |
when "100" => -- read rx descriptor pointer |
v_prdata(2 downto 0) := (others => '0'); |
v_prdata(31 downto 3) := r.rxdesc_ptr; |
when "101" => -- read tx descriptor pointer |
v_prdata(2 downto 0) := (others => '0'); |
v_prdata(31 downto 3) := r.txdesc_ptr; |
when others => |
null; |
end case; |
end if; |
|
-- APB write access. |
if (apbi.psel(pindex) and apbi.penable and apbi.pwrite) = '1' then |
case apbi.paddr(4 downto 2) is |
when "000" => -- write control register |
v.ctl_reset := apbi.pwdata(0); |
v.ctl_resetdma := apbi.pwdata(1); |
v.ctl_linkstart := apbi.pwdata(2); |
v.ctl_autostart := apbi.pwdata(3); |
v.ctl_linkdis := apbi.pwdata(4); |
v.ctl_ticken := apbi.pwdata(5); |
v.ctl_rxstart := apbi.pwdata(6); |
v.ctl_txstart := apbi.pwdata(7); |
if apbi.pwdata(8) = '1' then v.ctl_txcancel := '1'; end if; |
v.ctl_ielink := apbi.pwdata(9); |
v.ctl_ietick := apbi.pwdata(10); |
v.ctl_ierxdesc := apbi.pwdata(11); |
v.ctl_ietxdesc := apbi.pwdata(12); |
v.ctl_ierxpacket := apbi.pwdata(13); |
when "001" => -- write status register |
if apbi.pwdata(2) = '1' then v.sta_errdisc := '0'; end if; |
if apbi.pwdata(3) = '1' then v.sta_errpar := '0'; end if; |
if apbi.pwdata(4) = '1' then v.sta_erresc := '0'; end if; |
if apbi.pwdata(5) = '1' then v.sta_errcred := '0'; end if; |
if apbi.pwdata(10) = '1' then v.sta_gottick := '0'; end if; |
if apbi.pwdata(11) = '1' then v.sta_rxdesc := '0'; end if; |
if apbi.pwdata(12) = '1' then v.sta_txdesc := '0'; end if; |
if apbi.pwdata(13) = '1' then v.sta_rxpacket := '0'; end if; |
when "010" => -- write transmission clock scaler |
v.txdivcnt := apbi.pwdata(7 downto 0); |
when "011" => -- write timecode register |
v.time_in := apbi.pwdata(13 downto 8); |
if apbi.pwdata(16) = '1' then v.tick_in := '1'; end if; |
when "100" => -- write rx descriptor pointer |
v.rxdesc_ptr := apbi.pwdata(31 downto 3); |
when "101" => -- write tx descriptor pointer |
v.txdesc_ptr := apbi.pwdata(31 downto 3); |
when others => |
null; |
end case; |
end if; |
|
-- Drive control signals to RX fifo. |
s_rxfifo_raddr <= v.rxfifo_raddr; -- new value of rxfifo_raddr |
s_rxfifo_wen <= r.rxfifo_write; |
s_rxfifo_waddr <= r.rxfifo_waddr; |
s_rxfifo_wdata <= r.rxfifo_wdata; |
|
-- Drive control signals to TX fifo. |
s_txfifo_raddr <= v.txfifo_raddr; -- new value of txfifo_raddr |
s_txfifo_wen <= msto.txfifo_write; |
s_txfifo_waddr <= r.txfifo_waddr; |
s_txfifo_wdata <= msto.txfifo_wdata; |
|
-- Drive inputs to spwlink. |
linki.autostart <= r.ctl_autostart; |
linki.linkstart <= r.ctl_linkstart; |
linki.linkdis <= r.ctl_linkdis; |
linki.rxroom <= r.rxroom; |
linki.tick_in <= r.tick_in; |
linki.ctrl_in <= "00"; |
linki.time_in <= r.time_in; |
linki.txwrite <= (not r.txfifo_empty) and (not r.txdiscard); |
linki.txflag <= s_txfifo_rdata(35-v_txfifo_bytepos); |
linki.txdata <= s_txfifo_rdata(31-8*v_txfifo_bytepos downto 24-8*v_txfifo_bytepos); |
|
-- Drive divcnt input to spwxmit. |
if linko.running = '1' then |
xmit_divcnt <= r.txdivcnt; |
else |
xmit_divcnt <= default_divcnt; |
end if; |
|
-- Drive inputs to AHB master. |
msti.rxdma_start <= r.ctl_rxstart; |
msti.txdma_start <= r.ctl_txstart; |
msti.txdma_cancel <= r.ctl_txcancel; |
msti.rxdesc_ptr <= r.rxdesc_ptr; |
msti.txdesc_ptr <= r.txdesc_ptr; |
msti.rxfifo_rdata <= s_rxfifo_rdata; |
msti.rxfifo_empty <= r.rxfifo_empty; |
msti.rxfifo_nxempty <= v.rxfifo_empty; -- new value of rxfifo_empty |
msti.txfifo_nxfull <= r.txfifo_nxfull; |
msti.txfifo_highw <= r.txfifo_highw; |
|
-- Drive APB output signals. |
apbo.prdata <= v_prdata; |
apbo.pirq <= (others => '0'); |
apbo.pirq(pirq) <= r.irq; |
apbo.pconfig <= pconfig; |
apbo.pindex <= pindex; |
|
-- Reset components. |
ahbmst_rstn <= rstn and (not r.ctl_reset) and (not r.ctl_resetdma); |
s_rst <= (not rstn) or r.ctl_reset; |
|
-- Clear TX fifo on cancel request. |
if r.ctl_txcancel = '1' then |
v.txfifo_raddr := (others => '0'); |
v.txfifo_waddr := (others => '0'); |
v.txfifo_empty := '1'; |
v.txfifo_nxfull := '0'; |
v.txfifo_highw := '0'; |
v.txfifo_bytepos := "00"; |
v.txpacket := '0'; |
v.txdiscard := '0'; |
end if; |
|
-- Reset registers. |
if rstn = '0' or r.ctl_reset = '1' then |
v := regs_reset; |
end if; |
|
-- Update registers. |
rin <= v; |
end process; |
|
|
-- |
-- Update registers. |
-- |
process (clk) is |
begin |
if rising_edge(clk) then |
r <= rin; |
end if; |
end process; |
|
end architecture spwamba_arch; |
/spwambapkg.vhd
0,0 → 1,158
-- |
-- VHDL package for SpaceWire AMBA interface. |
-- |
-- This package depends on Gaisler GRLIB. |
-- |
|
library ieee; |
use ieee.std_logic_1164.all; |
library grlib; |
use grlib.amba.all; |
library techmap; |
use techmap.gencomp.all; |
use work.spwpkg.all; |
|
package spwambapkg is |
|
|
-- AMBA plug&play device id |
constant DEVICE_SPACEWIRELIGHT: amba_device_type := 16#131#; |
|
|
-- Signals from SpaceWire core to AHB master. |
type spw_ahbmst_in_type is record |
|
-- Pulse high to start the RX DMA engine. |
rxdma_start: std_ulogic; |
|
-- Pulse high to start the TX DMA engine. |
txdma_start: std_ulogic; |
|
-- Stop TX DMA engine (at end of current burst). |
txdma_cancel: std_ulogic; |
|
-- Address of current RX descriptor (8-byte aligned). |
rxdesc_ptr: std_logic_vector(31 downto 3); |
|
-- Address of current TX descriptor (8-byte aligned). |
txdesc_ptr: std_logic_vector(31 downto 3); |
|
-- Read port of RX FIFO. |
rxfifo_rdata: std_logic_vector(35 downto 0); |
|
-- High if RX FIFO is empty. |
rxfifo_empty: std_ulogic; |
|
-- High if RX FIFO will be empty after one read. |
-- May combinatorially depend on spw_ahbmst_out_type.rxfifo_read. |
rxfifo_nxempty: std_ulogic; |
|
-- High if TX FIFO is full or has room for at most one word. |
txfifo_nxfull: std_ulogic; |
|
-- High if TX FIFO is close to full (blocks refill). |
txfifo_highw: std_ulogic; |
end record; |
|
-- Signals from AHB master to SpaceWire core. |
type spw_ahbmst_out_type is record |
|
-- High if the RX DMA engine is enabled. |
rxdma_act: std_ulogic; |
|
-- High if the TX DMA engine is enabled. |
txdma_act: std_ulogic; |
|
-- High if an error occurred on the AHB bus. |
ahberror: std_ulogic; |
|
-- Pulsed high to trigger an RX descriptor interrupt. |
int_rxdesc: std_ulogic; |
|
-- Pulsed high to trigger a TX descriptor interrupt. |
int_txdesc: std_ulogic; |
|
-- Pulsed high when a complete packet has been received. |
int_rxpacket: std_ulogic; |
|
-- Pulsed high to request the next RX descriptor address. |
-- (rxdesc_ptr must be updated in the next clock cycle). |
rxdesc_next: std_ulogic; |
|
-- Pulsed high together with rxdesc_next to wrap the RX descriptor pointer. |
rxdesc_wrap: std_ulogic; |
|
-- Pulsed high to request the next TX descriptor address. |
-- (txdesc_ptr must be updated in the next clock cycle). |
txdesc_next: std_ulogic; |
|
-- Pulsed high together with txdesc_next to wrap the TX descriptor pointer. |
txdesc_wrap: std_ulogic; |
|
-- Read strobe to RX fifo. |
rxfifo_read: std_ulogic; |
|
-- Write enable to TX fifo. |
txfifo_write: std_ulogic; |
|
-- Input port of TX fifo. |
txfifo_wdata: std_logic_vector(35 downto 0); |
end record; |
|
|
-- SpaceWire core with AMBA interface. |
component spwamba is |
generic ( |
tech: integer range 0 to NTECH := DEFFABTECH; |
hindex: integer; -- AHB master index |
pindex: integer; -- APB slave index |
paddr: integer; -- APB address range |
pmask: integer := 16#fff#; -- APB address mask |
pirq: integer; -- interrupt number |
sysfreq: real; -- system clock frequency in Hz |
txclkfreq: real := 0.0; -- txclk frequency in Hz |
rximpl: spw_implementation_type := impl_generic; |
rxchunk: integer range 1 to 4 := 1; |
tximpl: spw_implementation_type := impl_generic; |
timecodegen: boolean := true; -- support timecode generation |
rxfifosize: integer range 6 to 12 := 8; -- size of receive FIFO (2-log of words) |
txfifosize: integer range 2 to 12 := 8; -- size of transmit FIFO (2-log of words) |
desctablesize: integer range 4 to 14 := 10; -- size of the DMA descriptor tables (2-log of descriptors) |
maxburst: integer range 1 to 8 := 3 -- max burst length (2-log of words) |
); |
port ( |
clk: in std_logic; -- system clock. |
rxclk: in std_logic; -- receiver sample clock |
txclk: in std_logic; -- transmit clock |
rstn: in std_logic; -- synchronous reset (active-low) |
apbi: in apb_slv_in_type; -- APB slave input signals |
apbo: out apb_slv_out_type; -- APB slave output signals |
ahbi: in ahb_mst_in_type; -- AHB master input signals |
ahbo: out ahb_mst_out_type; -- AHB master output signals |
tick_in: in std_logic; -- pulse for timecode generation |
spw_di: in std_logic; -- Data In signal from SpaceWire bus |
spw_si: in std_logic; -- Strobe In signal from SpaceWire bus |
spw_do: out std_logic; -- Data Out signal to SpaceWire bus |
spw_so: out std_logic -- Strobe Out signal to SpaceWire bus |
); |
end component spwamba; |
|
|
-- AHB master for AMBA interface. |
component spwahbmst is |
generic ( |
hindex: integer; -- AHB master index |
hconfig: ahb_config_type; -- AHB plug&play information |
maxburst: integer range 1 to 8 -- 2log of max burst length |
); |
port ( |
clk: in std_logic; -- system clock |
rstn: in std_logic; -- synchronous reset (active-low) |
msti: in spw_ahbmst_in_type; -- inputs from SpaceWire core |
msto: out spw_ahbmst_out_type; -- outputs to SpaceWire core |
ahbi: in ahb_mst_in_type; -- AHB master input signals |
ahbo: out ahb_mst_out_type -- AHB master output signals |
); |
end component spwahbmst; |
|
end package; |