|
|
--
|
|
|
-- 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;
|
|
|
|