spwrecv.vhd
267 lines
| 9.2 KiB
| text/x-vhdl
|
VhdlLexer
r681 | -- | |||
-- SpaceWire Receiver | ||||
-- | ||||
-- This entity decodes the sequence of incoming data bits into tokens. | ||||
-- Data bits are passed to this entity from the Receiver Front-end | ||||
-- in groups of rxchunk bits at a time. | ||||
-- | ||||
-- The bitrate of the incoming SpaceWire signal must be strictly less | ||||
-- than rxchunk times the system clock frequency. | ||||
-- | ||||
library ieee; | ||||
use ieee.std_logic_1164.all, ieee.numeric_std.all; | ||||
use work.spwpkg.all; | ||||
entity spwrecv is | ||||
generic ( | ||||
-- Disconnect timeout, expressed in system clock cycles. | ||||
-- Should be 850 ns (727 ns .. 1000 ns) according to the standard. | ||||
disconnect_time: integer range 1 to 255; | ||||
-- Nr of bits sampled per system clock. | ||||
rxchunk: integer range 1 to 4 | ||||
); | ||||
port ( | ||||
-- System clock. | ||||
clk: in std_logic; | ||||
-- High to enable receiver; low to disable and reset receiver. | ||||
rxen: in std_logic; | ||||
-- Output signals to spwlink. | ||||
recvo: out spw_recv_out_type; | ||||
-- High if there has been recent activity on the input lines. | ||||
inact: in std_logic; | ||||
-- High if inbits contains a valid group of received bits. | ||||
inbvalid: in std_logic; | ||||
-- Received bits from receiver front-end. | ||||
inbits: in std_logic_vector(rxchunk-1 downto 0) | ||||
); | ||||
end entity spwrecv; | ||||
architecture spwrecv_arch of spwrecv is | ||||
-- registers | ||||
type regs_type is record | ||||
-- receiver state | ||||
bit_seen: std_ulogic; -- got a bit transition | ||||
null_seen: std_ulogic; -- got a NULL token | ||||
-- input shift register | ||||
bitshift: std_logic_vector(8 downto 0); | ||||
bitcnt: std_logic_vector(9 downto 0); -- one-hot counter | ||||
-- parity flag | ||||
parity: std_ulogic; | ||||
-- decoding | ||||
control: std_ulogic; -- next code is control code | ||||
escaped: std_ulogic; -- last code was ESC | ||||
-- output registers | ||||
gotfct: std_ulogic; | ||||
tick_out: std_ulogic; | ||||
rxchar: std_ulogic; | ||||
rxflag: std_ulogic; | ||||
timereg: std_logic_vector(7 downto 0); | ||||
datareg: std_logic_vector(7 downto 0); | ||||
-- disconnect timer | ||||
disccnt: unsigned(7 downto 0); | ||||
-- error flags | ||||
errpar: std_ulogic; | ||||
erresc: std_ulogic; | ||||
end record; | ||||
-- Initial state | ||||
constant regs_reset: regs_type := ( | ||||
bit_seen => '0', | ||||
null_seen => '0', | ||||
bitshift => (others => '1'), | ||||
bitcnt => (others => '0'), | ||||
parity => '0', | ||||
control => '0', | ||||
escaped => '0', | ||||
gotfct => '0', | ||||
tick_out => '0', | ||||
rxchar => '0', | ||||
rxflag => '0', | ||||
timereg => (others => '0'), | ||||
datareg => (others => '0'), | ||||
disccnt => "00000000", | ||||
errpar => '0', | ||||
erresc => '0' ); | ||||
-- registers | ||||
signal r: regs_type := regs_reset; | ||||
signal rin: regs_type; | ||||
begin | ||||
-- combinatorial process | ||||
process (r, rxen, inact, inbvalid, inbits) | ||||
variable v: regs_type; | ||||
variable v_inbit: std_ulogic; | ||||
begin | ||||
v := r; | ||||
v_inbit := '0'; | ||||
-- disconnect timer | ||||
if inact = '1' then | ||||
-- activity on input; reset timer | ||||
v.disccnt := to_unsigned(disconnect_time, v.disccnt'length); | ||||
elsif r.disccnt /= 0 then | ||||
-- count down | ||||
v.disccnt := r.disccnt - 1; | ||||
end if; | ||||
-- assume no new token | ||||
v.gotfct := '0'; | ||||
v.tick_out := '0'; | ||||
v.rxchar := '0'; | ||||
if inbvalid = '1' then | ||||
-- process incoming bits | ||||
for i in 0 to rxchunk-1 loop | ||||
v_inbit := inbits(i); | ||||
-- got a bit transition | ||||
v.bit_seen := '1'; | ||||
if v.bitcnt(0) = '1' then | ||||
-- received new token | ||||
-- note that this will not happen before null_seen='1' | ||||
if (v.parity xor v_inbit) = '0' then | ||||
-- Parity check failed. | ||||
v.errpar := '1'; | ||||
else | ||||
if v.control = '1' then | ||||
-- received control code | ||||
case v.bitshift(7 downto 6) is | ||||
when "00" => -- FCT or NULL | ||||
v.gotfct := not r.escaped; | ||||
v.escaped := '0'; | ||||
when "10" => -- EOP | ||||
if r.escaped = '1' then | ||||
v.erresc := '1'; | ||||
end if; | ||||
v.escaped := '0'; | ||||
v.rxchar := not r.escaped; | ||||
v.rxflag := '1'; | ||||
v.datareg := "00000000"; | ||||
when "01" => -- EEP | ||||
if r.escaped = '1' then | ||||
v.erresc := '1'; | ||||
end if; | ||||
v.escaped := '0'; | ||||
v.rxchar := not r.escaped; | ||||
v.rxflag := '1'; | ||||
v.datareg := "00000001"; | ||||
when others => -- ESC | ||||
if r.escaped = '1' then | ||||
v.erresc := '1'; | ||||
end if; | ||||
v.escaped := '1'; | ||||
end case; | ||||
else | ||||
-- received 8-bit character | ||||
if r.escaped = '1' then | ||||
-- received Time-Code | ||||
v.tick_out := '1'; | ||||
v.timereg := v.bitshift(7 downto 0); | ||||
else | ||||
-- received data character | ||||
v.rxflag := '0'; | ||||
v.rxchar := '1'; | ||||
v.datareg := v.bitshift(7 downto 0); | ||||
end if; | ||||
v.escaped := '0'; | ||||
end if; | ||||
end if; | ||||
-- prepare for next code | ||||
v.parity := '0'; | ||||
v.control := v_inbit; | ||||
if v_inbit = '1' then | ||||
-- next word will be control code. | ||||
v.bitcnt := (3 => '1', others => '0'); | ||||
else | ||||
-- next word will be a data byte. | ||||
v.bitcnt := (9 => '1', others => '0'); | ||||
end if; | ||||
else | ||||
-- wait until next code is completely received; | ||||
-- accumulate parity | ||||
v.bitcnt := '0' & v.bitcnt(9 downto 1); | ||||
v.parity := v.parity xor v_inbit; | ||||
end if; | ||||
-- detect first NULL | ||||
if v.null_seen = '0' then | ||||
if v.bitshift = "000101110" then | ||||
-- got first NULL pattern | ||||
v.null_seen := '1'; | ||||
v.control := v_inbit; -- should always be '1' | ||||
v.parity := '0'; | ||||
v.bitcnt := (3 => '1', others => '0'); | ||||
end if; | ||||
end if; | ||||
-- shift new bit into register. | ||||
v.bitshift := v_inbit & v.bitshift(v.bitshift'high downto 1); | ||||
end loop; | ||||
end if; | ||||
-- synchronous reset | ||||
if rxen = '0' then | ||||
v.bit_seen := '0'; | ||||
v.null_seen := '0'; | ||||
v.bitshift := "111111111"; | ||||
v.bitcnt := (others => '0'); | ||||
v.gotfct := '0'; | ||||
v.tick_out := '0'; | ||||
v.rxchar := '0'; | ||||
v.rxflag := '0'; | ||||
v.escaped := '0'; | ||||
v.timereg := "00000000"; | ||||
v.datareg := "00000000"; | ||||
v.disccnt := to_unsigned(0, v.disccnt'length); | ||||
v.errpar := '0'; | ||||
v.erresc := '0'; | ||||
end if; | ||||
-- drive outputs | ||||
recvo.gotbit <= r.bit_seen; | ||||
recvo.gotnull <= r.null_seen; | ||||
recvo.gotfct <= r.gotfct; | ||||
recvo.tick_out <= r.tick_out; | ||||
recvo.ctrl_out <= r.timereg(7 downto 6); | ||||
recvo.time_out <= r.timereg(5 downto 0); | ||||
recvo.rxchar <= r.rxchar; | ||||
recvo.rxflag <= r.rxflag; | ||||
recvo.rxdata <= r.datareg; | ||||
if r.bit_seen = '1' and r.disccnt = 0 then | ||||
recvo.errdisc <= '1'; | ||||
else | ||||
recvo.errdisc <= '0'; | ||||
end if; | ||||
recvo.errpar <= r.errpar; | ||||
recvo.erresc <= r.erresc; | ||||
-- update registers | ||||
rin <= v; | ||||
end process; | ||||
-- update registers on rising edge of system clock | ||||
process (clk) is | ||||
begin | ||||
if rising_edge(clk) then | ||||
r <= rin; | ||||
end if; | ||||
end process; | ||||
end architecture spwrecv_arch; | ||||