--
|
--
|
-- SpaceWire Transmitter
|
-- SpaceWire Transmitter
|
--
|
--
|
-- This entity translates outgoing characters and tokens into
|
-- This entity translates outgoing characters and tokens into
|
-- data-strobe signalling.
|
-- data-strobe signalling.
|
--
|
--
|
-- The output stage is driven by a separate transmission clock "txclk" which
|
-- The output stage is driven by a separate transmission clock "txclk" which
|
-- will typically be faster than the system clock. The actual transmission
|
-- will typically be faster than the system clock. The actual transmission
|
-- rate is determined by dividing the transmission clock by an integer factor.
|
-- rate is determined by dividing the transmission clock by an integer factor.
|
--
|
--
|
-- The code is tuned for implementation on Xilinx Spartan-3.
|
-- The code is tuned for implementation on Xilinx Spartan-3.
|
--
|
--
|
-- Concept
|
-- Concept
|
-- -------
|
-- -------
|
--
|
--
|
-- Logic in the system clock domain generates a stream of tokens to be
|
-- Logic in the system clock domain generates a stream of tokens to be
|
-- transmitted. These tokens are encoded as instances of the token_type
|
-- transmitted. These tokens are encoded as instances of the token_type
|
-- record. Tokens are queued in a two-slot FIFO buffer (r.token0 and r.token1)
|
-- record. Tokens are queued in a two-slot FIFO buffer (r.token0 and r.token1)
|
-- with a 1-bit pointer (r.tokmux) pointing to the head of the queue.
|
-- with a 1-bit pointer (r.tokmux) pointing to the head of the queue.
|
-- When a token is pushed into the buffer, a flag register is flipped
|
-- When a token is pushed into the buffer, a flag register is flipped
|
-- (r.sysflip0 and r.sysflip1) to indicate to the txclk domain that the
|
-- (r.sysflip0 and r.sysflip1) to indicate to the txclk domain that the
|
-- buffer slot has been refilled.
|
-- buffer slot has been refilled.
|
--
|
--
|
-- The txclk domain pulls tokens from the FIFO buffer, flipping flag
|
-- The txclk domain pulls tokens from the FIFO buffer, flipping flag
|
-- registers (rtx.txflip0 and rtx.txflip1) to indicate to the system clock
|
-- registers (rtx.txflip0 and rtx.txflip1) to indicate to the system clock
|
-- domain that a token has been pulled. When the system clock domain detects
|
-- domain that a token has been pulled. When the system clock domain detects
|
-- that a token has been consumed, it refills the buffer slot with a new
|
-- that a token has been consumed, it refills the buffer slot with a new
|
-- token (assuming that there are tokens waiting to be transmitted).
|
-- token (assuming that there are tokens waiting to be transmitted).
|
-- Whenever the FIFO buffer is empty, the txclk domain sends NULLs instead.
|
-- Whenever the FIFO buffer is empty, the txclk domain sends NULLs instead.
|
-- This can happen either when there are no tokens to send, or when the
|
-- This can happen either when there are no tokens to send, or when the
|
-- system clock domain is late to refill the buffer.
|
-- system clock domain is late to refill the buffer.
|
--
|
--
|
-- Details
|
-- Details
|
-- -------
|
-- -------
|
--
|
--
|
-- Logic in the system clock domain accepts transmission requests through
|
-- Logic in the system clock domain accepts transmission requests through
|
-- the external interface of the entity. Pending requests are translated
|
-- the external interface of the entity. Pending requests are translated
|
-- into a stream of tokens. The tokens are pushed to the txclk domain through
|
-- into a stream of tokens. The tokens are pushed to the txclk domain through
|
-- the FIFO buffer as described above.
|
-- the FIFO buffer as described above.
|
--
|
--
|
-- The data path through the txclk domain is divided into stages A through F
|
-- The data path through the txclk domain is divided into stages A through F
|
-- in a half-hearted attempt to keep things simple. Stage A is just a
|
-- in a half-hearted attempt to keep things simple. Stage A is just a
|
-- synchronizer for the buffer status flags from the system clock domain.
|
-- synchronizer for the buffer status flags from the system clock domain.
|
--
|
--
|
-- Stage B takes a token from the FIFO buffer and updates a buffer status
|
-- Stage B takes a token from the FIFO buffer and updates a buffer status
|
-- flag to indicate that the buffer slot needs to be refilled. If the FIFO
|
-- flag to indicate that the buffer slot needs to be refilled. If the FIFO
|
-- is empty, a NULL is inserted. Stage B is triggered one clock after
|
-- is empty, a NULL is inserted. Stage B is triggered one clock after
|
-- stage E switches to a new token. If the previous token was ESC, stage B
|
-- stage E switches to a new token. If the previous token was ESC, stage B
|
-- skips a turn because stage C will already know what to do.
|
-- skips a turn because stage C will already know what to do.
|
--
|
--
|
-- Stage C takes a token from stage B and translates it into a bit pattern.
|
-- Stage C takes a token from stage B and translates it into a bit pattern.
|
-- Time codes and NULL tokens are broken into two separate tokens starting
|
-- Time codes and NULL tokens are broken into two separate tokens starting
|
-- with ESC. Stage C is triggered one clock after the shift buffer in
|
-- with ESC. Stage C is triggered one clock after the shift buffer in
|
-- stage E drops to 3 tokens.
|
-- stage E drops to 3 tokens.
|
--
|
--
|
-- Stage D completes the task of translating tokens to bit patterns and
|
-- Stage D completes the task of translating tokens to bit patterns and
|
-- distinguishes between 10-bit and 4-bit tokens. It is not explicitly
|
-- distinguishes between 10-bit and 4-bit tokens. It is not explicitly
|
-- triggered but simply follows stage C.
|
-- triggered but simply follows stage C.
|
--
|
--
|
-- Stage E is the bit shift register. It shifts when "txclken" is high.
|
-- Stage E is the bit shift register. It shifts when "txclken" is high.
|
-- A one-hot counter keeps track of the number of bits remaining in
|
-- A one-hot counter keeps track of the number of bits remaining in
|
-- the register. When the register falls empty, it loads a new 10-bit or
|
-- the register. When the register falls empty, it loads a new 10-bit or
|
-- 4-bit pattern as prepared by stage D. Stage E also computes parity.
|
-- 4-bit pattern as prepared by stage D. Stage E also computes parity.
|
--
|
--
|
-- Stage F performs data strobe encoding. When the transmitter is disabled,
|
-- Stage F performs data strobe encoding. When the transmitter is disabled,
|
-- the outputs of stage F fall to zero in a controlled way.
|
-- the outputs of stage F fall to zero in a controlled way.
|
--
|
--
|
-- To generate the transmission bit clock, the txclk is divided by an
|
-- To generate the transmission bit clock, the txclk is divided by an
|
-- integer factor (divcnt+1) using an 8-bit down counter. The implementation
|
-- integer factor (divcnt+1) using an 8-bit down counter. The implementation
|
-- of this counter has become quite complicated in order to meet timing goals.
|
-- of this counter has become quite complicated in order to meet timing goals.
|
-- The counter consists of 4 blocks of two bits each (txclkcnt), with a
|
-- The counter consists of 4 blocks of two bits each (txclkcnt), with a
|
-- carry-save concept used between blocks (txclkcy). Detection of terminal
|
-- carry-save concept used between blocks (txclkcy). Detection of terminal
|
-- count (txclkdone) has a pipeline delay of two cycles. Therefore a separate
|
-- count (txclkdone) has a pipeline delay of two cycles. Therefore a separate
|
-- concept is used if the initial count is less than 2 (txdivnorm). This is
|
-- concept is used if the initial count is less than 2 (txdivnorm). This is
|
-- all glued together in the final assignment to txclken.
|
-- all glued together in the final assignment to txclken.
|
--
|
--
|
-- The initial count for txclk division (divcnt) comes from the system clock
|
-- The initial count for txclk division (divcnt) comes from the system clock
|
-- domain and thus needs to be synchronized for use in the txclk domain.
|
-- domain and thus needs to be synchronized for use in the txclk domain.
|
-- To facilitate this, the system clock domain latches the value of divcnt
|
-- To facilitate this, the system clock domain latches the value of divcnt
|
-- once every 6 sysclk cycles and sets a flag to indicate when the latched
|
-- once every 6 sysclk cycles and sets a flag to indicate when the latched
|
-- value can safely be used by the txclk domain.
|
-- value can safely be used by the txclk domain.
|
--
|
--
|
-- A tricky aspect of the design is the initial state of the txclk logic.
|
-- A tricky aspect of the design is the initial state of the txclk logic.
|
-- When the transmitter is enabled (txen goes high), the txclk logic starts
|
-- When the transmitter is enabled (txen goes high), the txclk logic starts
|
-- with the first ESC pattern already set up in stage D, and stage C ready
|
-- with the first ESC pattern already set up in stage D, and stage C ready
|
-- to produce the FCT part of the first NULL.
|
-- to produce the FCT part of the first NULL.
|
--
|
--
|
-- The following guidelines are used to get good timing for the txclk domain:
|
-- The following guidelines are used to get good timing for the txclk domain:
|
-- * The new value of a register depends on at most 4 inputs (single LUT),
|
-- * The new value of a register depends on at most 4 inputs (single LUT),
|
-- or in a few cases on 5 inputs (two LUTs and F5MUX).
|
-- or in a few cases on 5 inputs (two LUTs and F5MUX).
|
-- * Synchronous resets may be used, but only if the reset signal comes
|
-- * Synchronous resets may be used, but only if the reset signal comes
|
-- directly from a register (no logic in set/reset path);
|
-- directly from a register (no logic in set/reset path);
|
-- * Clock enables may be used, but only if the enable signal comes directly
|
-- * Clock enables may be used, but only if the enable signal comes directly
|
-- from a register (no logic in clock enable path).
|
-- from a register (no logic in clock enable path).
|
--
|
--
|
-- Synchronization issues
|
-- Synchronization issues
|
-- ----------------------
|
-- ----------------------
|
--
|
--
|
-- There is a two-slot FIFO buffer between the system and txclk domains.
|
-- There is a two-slot FIFO buffer between the system and txclk domains.
|
-- After the txclk domain pulls a token from the buffer, the system clock
|
-- After the txclk domain pulls a token from the buffer, the system clock
|
-- domain should ideally refill the buffer before the txclk domain again
|
-- domain should ideally refill the buffer before the txclk domain again
|
-- tries to pull from the same buffer slot. If the refill occurs late,
|
-- tries to pull from the same buffer slot. If the refill occurs late,
|
-- the txclk domain needs to insert a NULL token which is inefficient
|
-- the txclk domain needs to insert a NULL token which is inefficient
|
-- use of bandwidth.
|
-- use of bandwidth.
|
--
|
--
|
-- Assuming the transmission consists of a stream of data characters,
|
-- Assuming the transmission consists of a stream of data characters,
|
-- 10 bits per character, there are exactly 2*10 bit periods between
|
-- 10 bits per character, there are exactly 2*10 bit periods between
|
-- successive reads from the same buffer slot by the txclk logic.
|
-- successive reads from the same buffer slot by the txclk logic.
|
--
|
--
|
-- The time needed for the system clock logic to refill a buffer slot =
|
-- The time needed for the system clock logic to refill a buffer slot =
|
-- 1 txclk period (update of rtx.txflipN)
|
-- 1 txclk period (update of rtx.txflipN)
|
-- + 1 txclk period (routing delay between domains)
|
-- + 1 txclk period (routing delay between domains)
|
-- + 2 sysclk periods (synchronizer for txflipN)
|
-- + 2 sysclk periods (synchronizer for txflipN)
|
-- + 1 sysclk period (refill buffer slot and update r.sysflipN)
|
-- + 1 sysclk period (refill buffer slot and update r.sysflipN)
|
-- + 1 txclk period (routing delay between domains)
|
-- + 1 txclk period (routing delay between domains)
|
-- + 2 txclk periods (synchronizer for sysflipN)
|
-- + 2 txclk periods (synchronizer for sysflipN)
|
-- = 5 txclk periods + 3 sysclk periods
|
-- = 5 txclk periods + 3 sysclk periods
|
--
|
--
|
-- If for example txclk is 4 times as fast as sysclk, this amounts to
|
-- If for example txclk is 4 times as fast as sysclk, this amounts to
|
-- 5 txclk + 3 sysclk = 5 + 3*4 txclk = 17 txclk
|
-- 5 txclk + 3 sysclk = 5 + 3*4 txclk = 17 txclk
|
-- is less than 20 bit periods even at maximum transmission rate, so
|
-- is less than 20 bit periods even at maximum transmission rate, so
|
-- no problem there.
|
-- no problem there.
|
--
|
--
|
-- This is different when the data stream includes 4-bit tokens.
|
-- This is different when the data stream includes 4-bit tokens.
|
-- See the datasheet for an analysis of that case.
|
-- See the manual for further comments.
|
--
|
--
|
-- Implementation guidelines
|
-- Implementation guidelines
|
-- -------------------------
|
-- -------------------------
|
--
|
--
|
-- To minimize clock skew, IOB flip-flops should be used to drive
|
-- To minimize clock skew, IOB flip-flops should be used to drive
|
-- spw_do and spw_so.
|
-- spw_do and spw_so.
|
--
|
--
|
-- "txclk" must be at least as fast as the system clock;
|
-- "txclk" must be at least as fast as the system clock;
|
-- "txclk" does not need to be phase-related to the system clock;
|
-- "txclk" does not need to be phase-related to the system clock;
|
-- it is allowed for "txclk" to be equal to "clk".
|
-- it is allowed for "txclk" to be equal to "clk".
|
--
|
--
|
-- The following timing constraints are needed:
|
-- The following timing constraints are needed:
|
-- * PERIOD constraint on the system clock;
|
-- * PERIOD constraint on the system clock;
|
-- * PERIOD constraint on "txclk";
|
-- * PERIOD constraint on "txclk";
|
-- * FROM-TO constraint from "txclk" to the system clock, equal to
|
-- * FROM-TO constraint from "txclk" to the system clock, equal to
|
-- one "txclk" period;
|
-- one "txclk" period;
|
-- * FROM-TO constraint from the system clock to "txclk", equal to
|
-- * FROM-TO constraint from the system clock to "txclk", equal to
|
-- one "txclk" period.
|
-- one "txclk" period.
|
--
|
--
|
|
|
library ieee;
|
library ieee;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_1164.all;
|
use ieee.numeric_std.all;
|
use ieee.numeric_std.all;
|
use work.spwpkg.all;
|
use work.spwpkg.all;
|
|
|
entity spwxmit_fast is
|
entity spwxmit_fast is
|
|
|
port (
|
port (
|
-- System clock.
|
-- System clock.
|
clk: in std_logic;
|
clk: in std_logic;
|
|
|
-- Transmit clock.
|
-- Transmit clock.
|
txclk: in std_logic;
|
txclk: in std_logic;
|
|
|
-- Synchronous reset (active-high)
|
-- Synchronous reset (active-high)
|
-- Used asynchronously by fast clock domain (must be glitch-free).
|
-- Used asynchronously by fast clock domain (must be glitch-free).
|
rst: in std_logic;
|
rst: in std_logic;
|
|
|
-- Scaling factor minus 1, used to scale the system clock into the
|
-- Scaling factor minus 1, used to scale the system clock into the
|
-- transmission bit rate. The system clock is divided by
|
-- transmission bit rate. The system clock is divided by
|
-- (unsigned(divcnt) + 1). Changing this signal will immediately
|
-- (unsigned(divcnt) + 1). Changing this signal will immediately
|
-- change the transmission rate.
|
-- change the transmission rate.
|
divcnt: in std_logic_vector(7 downto 0);
|
divcnt: in std_logic_vector(7 downto 0);
|
|
|
-- Input signals from spwlink.
|
-- Input signals from spwlink.
|
xmiti: in spw_xmit_in_type;
|
xmiti: in spw_xmit_in_type;
|
|
|
-- Output signals to spwlink.
|
-- Output signals to spwlink.
|
xmito: out spw_xmit_out_type;
|
xmito: out spw_xmit_out_type;
|
|
|
-- Data Out signal to SpaceWire bus.
|
-- Data Out signal to SpaceWire bus.
|
spw_do: out std_logic;
|
spw_do: out std_logic;
|
|
|
-- Strobe Out signal to SpaceWire bus.
|
-- Strobe Out signal to SpaceWire bus.
|
spw_so: out std_logic
|
spw_so: out std_logic
|
);
|
);
|
|
|
-- Turn off FSM extraction to avoid synchronization problems.
|
-- Turn off FSM extraction to avoid synchronization problems.
|
attribute FSM_EXTRACT: string;
|
attribute FSM_EXTRACT: string;
|
attribute FSM_EXTRACT of spwxmit_fast: entity is "NO";
|
attribute FSM_EXTRACT of spwxmit_fast: entity is "NO";
|
|
|
-- Turn off register duplication to avoid synchronization problems.
|
-- Turn off register duplication to avoid synchronization problems.
|
attribute REGISTER_DUPLICATION: string;
|
attribute REGISTER_DUPLICATION: string;
|
attribute REGISTER_DUPLICATION of spwxmit_fast: entity is "FALSE";
|
attribute REGISTER_DUPLICATION of spwxmit_fast: entity is "FALSE";
|
|
|
end entity spwxmit_fast;
|
end entity spwxmit_fast;
|
|
|
architecture spwxmit_fast_arch of spwxmit_fast is
|
architecture spwxmit_fast_arch of spwxmit_fast is
|
|
|
-- Convert boolean to std_logic.
|
-- Convert boolean to std_logic.
|
type bool_to_logic_type is array(boolean) of std_ulogic;
|
type bool_to_logic_type is array(boolean) of std_ulogic;
|
constant bool_to_logic: bool_to_logic_type := (false => '0', true => '1');
|
constant bool_to_logic: bool_to_logic_type := (false => '0', true => '1');
|
|
|
-- Data records passed between clock domains.
|
-- Data records passed between clock domains.
|
type token_type is record
|
type token_type is record
|
tick: std_ulogic; -- send time code
|
tick: std_ulogic; -- send time code
|
fct: std_ulogic; -- send FCT
|
fct: std_ulogic; -- send FCT
|
|
fctpiggy: std_ulogic; -- send FCT and N-char
|
flag: std_ulogic; -- send EOP or EEP
|
flag: std_ulogic; -- send EOP or EEP
|
char: std_logic_vector(7 downto 0); -- character or time code
|
char: std_logic_vector(7 downto 0); -- character or time code
|
end record;
|
end record;
|
|
|
-- Registers in txclk domain
|
-- Registers in txclk domain
|
type txregs_type is record
|
type txregs_type is record
|
-- sync to system clock domain
|
-- sync to system clock domain
|
txflip0: std_ulogic;
|
txflip0: std_ulogic;
|
txflip1: std_ulogic;
|
txflip1: std_ulogic;
|
-- stage A
|
-- stage A
|
a_sysflip0: std_logic_vector(1 downto 0);
|
a_sysflip0: std_logic_vector(1 downto 0);
|
a_sysflip1: std_logic_vector(1 downto 0);
|
a_sysflip1: std_logic_vector(1 downto 0);
|
-- stage B
|
-- stage B
|
b_update: std_ulogic;
|
b_update: std_ulogic;
|
b_mux: std_ulogic;
|
b_mux: std_ulogic;
|
b_txflip: std_ulogic;
|
b_txflip: std_ulogic;
|
b_valid: std_ulogic;
|
b_valid: std_ulogic;
|
b_token: token_type;
|
b_token: token_type;
|
-- stage C
|
-- stage C
|
c_update: std_ulogic;
|
c_update: std_ulogic;
|
|
c_busy: std_ulogic;
|
c_esc: std_ulogic;
|
c_esc: std_ulogic;
|
c_fct: std_ulogic;
|
c_fct: std_ulogic;
|
c_bits: std_logic_vector(8 downto 0);
|
c_bits: std_logic_vector(8 downto 0);
|
-- stage D
|
-- stage D
|
d_bits: std_logic_vector(8 downto 0);
|
d_bits: std_logic_vector(8 downto 0);
|
d_cnt4: std_ulogic;
|
d_cnt4: std_ulogic;
|
d_cnt10: std_ulogic;
|
d_cnt10: std_ulogic;
|
-- stage E
|
-- stage E
|
e_valid: std_ulogic;
|
e_valid: std_ulogic;
|
e_shift: std_logic_vector(9 downto 0);
|
e_shift: std_logic_vector(9 downto 0);
|
e_count: std_logic_vector(9 downto 0);
|
e_count: std_logic_vector(9 downto 0);
|
e_parity: std_ulogic;
|
e_parity: std_ulogic;
|
-- stage F
|
-- stage F
|
f_spwdo: std_ulogic;
|
f_spwdo: std_ulogic;
|
f_spwso: std_ulogic;
|
f_spwso: std_ulogic;
|
-- tx clock enable logic
|
-- tx clock enable logic
|
txclken: std_ulogic;
|
txclken: std_ulogic;
|
txclkpre: std_ulogic;
|
txclkpre: std_ulogic;
|
txclkcnt: std_logic_vector(7 downto 0);
|
txclkcnt: std_logic_vector(7 downto 0);
|
txclkcy: std_logic_vector(2 downto 0);
|
txclkcy: std_logic_vector(2 downto 0);
|
txclkdone: std_logic_vector(1 downto 0);
|
txclkdone: std_logic_vector(1 downto 0);
|
txclkdiv: std_logic_vector(7 downto 0);
|
txclkdiv: std_logic_vector(7 downto 0);
|
txdivnorm: std_ulogic;
|
txdivnorm: std_ulogic;
|
txdivsafe: std_logic_vector(1 downto 0);
|
txdivsafe: std_logic_vector(1 downto 0);
|
-- tx enable logic
|
-- tx enable logic
|
txensync: std_logic_vector(1 downto 0);
|
txensync: std_logic_vector(1 downto 0);
|
end record;
|
end record;
|
|
|
-- Registers in system clock domain
|
-- Registers in system clock domain
|
type regs_type is record
|
type regs_type is record
|
-- sync status to txclk domain
|
-- sync status to txclk domain
|
txenreg: std_ulogic;
|
txenreg: std_ulogic;
|
txdivreg: std_logic_vector(7 downto 0);
|
txdivreg: std_logic_vector(7 downto 0);
|
txdivnorm: std_ulogic;
|
txdivnorm: std_ulogic;
|
txdivtmp: std_logic_vector(1 downto 0);
|
txdivtmp: std_logic_vector(1 downto 0);
|
txdivsafe: std_ulogic;
|
txdivsafe: std_ulogic;
|
-- data stream to txclk domain
|
-- data stream to txclk domain
|
sysflip0: std_ulogic;
|
sysflip0: std_ulogic;
|
sysflip1: std_ulogic;
|
sysflip1: std_ulogic;
|
token0: token_type;
|
token0: token_type;
|
token1: token_type;
|
token1: token_type;
|
tokmux: std_ulogic;
|
tokmux: std_ulogic;
|
-- sync feedback from txclk domain
|
-- sync feedback from txclk domain
|
txflip0: std_logic_vector(1 downto 0);
|
txflip0: std_logic_vector(1 downto 0);
|
txflip1: std_logic_vector(1 downto 0);
|
txflip1: std_logic_vector(1 downto 0);
|
-- transmitter management
|
-- transmitter management
|
pend_fct: std_ulogic; -- '1' if an outgoing FCT is pending
|
pend_fct: std_ulogic; -- '1' if an outgoing FCT is pending
|
pend_char: std_ulogic; -- '1' if an outgoing N-Char is pending
|
pend_char: std_ulogic; -- '1' if an outgoing N-Char is pending
|
pend_data: std_logic_vector(8 downto 0); -- control flag and data bits of pending char
|
pend_data: std_logic_vector(8 downto 0); -- control flag and data bits of pending char
|
pend_tick: std_ulogic; -- '1' if an outgoing time tick is pending
|
pend_tick: std_ulogic; -- '1' if an outgoing time tick is pending
|
pend_time: std_logic_vector(7 downto 0); -- data bits of pending time tick
|
pend_time: std_logic_vector(7 downto 0); -- data bits of pending time tick
|
allow_fct: std_ulogic; -- '1' when allowed to send FCTs
|
allow_fct: std_ulogic; -- '1' when allowed to send FCTs
|
allow_char: std_ulogic; -- '1' when allowed to send data and time
|
allow_char: std_ulogic; -- '1' when allowed to send data and time
|
sent_fct: std_ulogic; -- '1' when at least one FCT token was sent
|
sent_fct: std_ulogic; -- '1' when at least one FCT token was sent
|
end record;
|
end record;
|
|
|
-- Initial state of system clock domain
|
-- Initial state of system clock domain
|
|
constant token_reset: token_type := (
|
|
tick => '0',
|
|
fct => '0',
|
|
fctpiggy => '0',
|
|
flag => '0',
|
|
char => (others => '0') );
|
constant regs_reset: regs_type := (
|
constant regs_reset: regs_type := (
|
txenreg => '0',
|
txenreg => '0',
|
txdivreg => (others => '0'),
|
txdivreg => (others => '0'),
|
txdivnorm => '0',
|
txdivnorm => '0',
|
txdivtmp => "00",
|
txdivtmp => "00",
|
txdivsafe => '0',
|
txdivsafe => '0',
|
sysflip0 => '0',
|
sysflip0 => '0',
|
sysflip1 => '0',
|
sysflip1 => '0',
|
token0 => ( tick => '0', fct => '0', flag => '0', char => (others => '0') ),
|
token0 => token_reset,
|
token1 => ( tick => '0', fct => '0', flag => '0', char => (others => '0') ),
|
token1 => token_reset,
|
tokmux => '0',
|
tokmux => '0',
|
txflip0 => "00",
|
txflip0 => "00",
|
txflip1 => "00",
|
txflip1 => "00",
|
pend_fct => '0',
|
pend_fct => '0',
|
pend_char => '0',
|
pend_char => '0',
|
pend_data => (others => '0'),
|
pend_data => (others => '0'),
|
pend_tick => '0',
|
pend_tick => '0',
|
pend_time => (others => '0'),
|
pend_time => (others => '0'),
|
allow_fct => '0',
|
allow_fct => '0',
|
allow_char => '0',
|
allow_char => '0',
|
sent_fct => '0' );
|
sent_fct => '0' );
|
|
|
-- Registers
|
-- Registers
|
signal rtx: txregs_type;
|
signal rtx: txregs_type;
|
signal rtxin: txregs_type;
|
signal rtxin: txregs_type;
|
signal r: regs_type := regs_reset;
|
signal r: regs_type := regs_reset;
|
signal rin: regs_type;
|
signal rin: regs_type;
|
|
|
-- Reset synchronizer for txclk domain
|
-- Reset synchronizer for txclk domain
|
signal s_tx_rst_sync: std_logic_vector(1 downto 0) := "11";
|
signal s_tx_rst_sync: std_logic_vector(1 downto 0) := "11";
|
signal s_tx_reset: std_ulogic := '1';
|
signal s_tx_reset: std_ulogic := '1';
|
|
|
-- Output flip-flops
|
-- Output flip-flops
|
signal s_spwdo: std_logic;
|
signal s_spwdo: std_logic;
|
signal s_spwso: std_logic;
|
signal s_spwso: std_logic;
|
|
|
-- Force use of IOB flip-flops
|
-- Force use of IOB flip-flops
|
attribute IOB: string;
|
attribute IOB: string;
|
attribute IOB of s_spwdo: signal is "TRUE";
|
attribute IOB of s_spwdo: signal is "TRUE";
|
attribute IOB of s_spwso: signal is "TRUE";
|
attribute IOB of s_spwso: signal is "TRUE";
|
|
|
begin
|
begin
|
|
|
-- Drive SpaceWire output signals
|
-- Drive SpaceWire output signals
|
spw_do <= s_spwdo;
|
spw_do <= s_spwdo;
|
spw_so <= s_spwso;
|
spw_so <= s_spwso;
|
|
|
-- Combinatorial process
|
-- Combinatorial process
|
process (r, rtx, rst, divcnt, xmiti, s_tx_reset) is
|
process (r, rtx, rst, divcnt, xmiti, s_tx_reset) is
|
variable v: regs_type;
|
variable v: regs_type;
|
variable vtx: txregs_type;
|
variable vtx: txregs_type;
|
variable v_needtoken: std_ulogic;
|
variable v_needtoken: std_ulogic;
|
variable v_havetoken: std_ulogic;
|
variable v_havetoken: std_ulogic;
|
variable v_token: token_type;
|
variable v_token: token_type;
|
begin
|
begin
|
v := r;
|
v := r;
|
vtx := rtx;
|
vtx := rtx;
|
v_needtoken := '0';
|
v_needtoken := '0';
|
v_havetoken := '0';
|
v_havetoken := '0';
|
v_token := ( tick => '0', fct => '0', flag => '0', char => (others => '0') );
|
v_token := token_reset;
|
|
|
-- ---- FAST CLOCK DOMAIN ----
|
-- ---- FAST CLOCK DOMAIN ----
|
|
|
-- Stage A: Synchronize token buffer counts from system clock domain.
|
-- Stage A: Synchronize token buffer counts from system clock domain.
|
vtx.a_sysflip0(0) := r.sysflip0;
|
vtx.a_sysflip0(0) := r.sysflip0;
|
vtx.a_sysflip0(1) := rtx.a_sysflip0(0);
|
vtx.a_sysflip0(1) := rtx.a_sysflip0(0);
|
vtx.a_sysflip1(0) := r.sysflip1;
|
vtx.a_sysflip1(0) := r.sysflip1;
|
vtx.a_sysflip1(1) := rtx.a_sysflip1(0);
|
vtx.a_sysflip1(1) := rtx.a_sysflip1(0);
|
|
|
-- Stage B: Multiplex tokens from system clock domain.
|
-- Stage B: Multiplex tokens from system clock domain.
|
-- Update stage B three bit periods after updating stage C
|
-- Update stage B three bit periods after updating stage C
|
-- (i.e. in time for the next update of stage C).
|
-- (i.e. in time for the next update of stage C).
|
-- Do not update stage B if the last token from stage C was ESC;
|
-- Do not update stage B if stage C is indicating that it needs to
|
-- stage C already knows what token to put after the ESC.
|
-- send a second token to complete its task.
|
vtx.b_update := rtx.txclken and rtx.e_count(0) and (not rtx.c_esc);
|
vtx.b_update := rtx.txclken and rtx.e_count(0) and (not rtx.c_busy);
|
if rtx.b_mux = '0' then
|
if rtx.b_mux = '0' then
|
vtx.b_txflip := rtx.txflip0;
|
vtx.b_txflip := rtx.txflip0;
|
else
|
else
|
vtx.b_txflip := rtx.txflip1;
|
vtx.b_txflip := rtx.txflip1;
|
end if;
|
end if;
|
if rtx.b_update = '1' then
|
if rtx.b_update = '1' then
|
if rtx.b_mux = '0' then
|
if rtx.b_mux = '0' then
|
-- get token from slot 0
|
-- get token from slot 0
|
vtx.b_valid := rtx.a_sysflip0(1) xor rtx.b_txflip;
|
vtx.b_valid := rtx.a_sysflip0(1) xor rtx.b_txflip;
|
vtx.b_token := r.token0;
|
vtx.b_token := r.token0;
|
-- update mux flag if we got a valid token
|
-- update mux flag if we got a valid token
|
vtx.b_mux := rtx.a_sysflip0(1) xor rtx.b_txflip;
|
vtx.b_mux := rtx.a_sysflip0(1) xor rtx.b_txflip;
|
vtx.txflip0 := rtx.a_sysflip0(1);
|
vtx.txflip0 := rtx.a_sysflip0(1);
|
vtx.txflip1 := rtx.txflip1;
|
vtx.txflip1 := rtx.txflip1;
|
else
|
else
|
-- get token from slot 1
|
-- get token from slot 1
|
vtx.b_valid := rtx.a_sysflip1(1) xor rtx.b_txflip;
|
vtx.b_valid := rtx.a_sysflip1(1) xor rtx.b_txflip;
|
vtx.b_token := r.token1;
|
vtx.b_token := r.token1;
|
-- update mux flag if we got a valid token
|
-- update mux flag if we got a valid token
|
vtx.b_mux := not (rtx.a_sysflip1(1) xor rtx.b_txflip);
|
vtx.b_mux := not (rtx.a_sysflip1(1) xor rtx.b_txflip);
|
vtx.txflip0 := rtx.txflip0;
|
vtx.txflip0 := rtx.txflip0;
|
vtx.txflip1 := rtx.a_sysflip1(1);
|
vtx.txflip1 := rtx.a_sysflip1(1);
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
-- Stage C: Prepare to transmit EOP, EEP or a data character.
|
-- Stage C: Prepare to transmit EOP, EEP or a data character.
|
vtx.c_update := rtx.txclken and rtx.e_count(3);
|
vtx.c_update := rtx.txclken and rtx.e_count(3);
|
if rtx.c_update = '1' then
|
if rtx.c_update = '1' then
|
-- NULL is broken into two tokens: ESC + FCT
|
|
-- time codes are broken into two tokens: ESC + char
|
-- NULL is broken into two tokens: ESC + FCT.
|
|
-- Time-codes are broken into two tokens: ESC + char.
|
|
|
|
-- Enable c_esc on the first pass of a NULL or a time-code.
|
vtx.c_esc := (rtx.b_token.tick or (not rtx.b_valid)) and
|
vtx.c_esc := (rtx.b_token.tick or (not rtx.b_valid)) and
|
(not rtx.c_esc);
|
(not rtx.c_esc);
|
vtx.c_fct := rtx.b_token.fct or (not rtx.b_valid);
|
|
|
-- Enable c_fct on the first pass of an FCT and on
|
|
-- the second pass of a NULL (also the first pass, but c_esc
|
|
-- is stronger than c_fct).
|
|
vtx.c_fct := (rtx.b_token.fct and (not rtx.c_busy)) or
|
|
(not rtx.b_valid);
|
|
|
|
-- Enable c_busy on the first pass of a NULL or a time-code
|
|
-- or a piggy-backed FCT. This will tell stage B that we are
|
|
-- not done yet.
|
|
vtx.c_busy := (rtx.b_token.tick or (not rtx.b_valid) or
|
|
rtx.b_token.fctpiggy) and (not rtx.c_busy);
|
|
|
if rtx.b_token.flag = '1' then
|
if rtx.b_token.flag = '1' then
|
if rtx.b_token.char(0) = '0' then
|
if rtx.b_token.char(0) = '0' then
|
-- prepare to send EOP
|
-- prepare to send EOP
|
vtx.c_bits := "000000101"; -- EOP = P101
|
vtx.c_bits := "000000101"; -- EOP = P101
|
else
|
else
|
-- prepare to send EEP
|
-- prepare to send EEP
|
vtx.c_bits := "000000011"; -- EEP = P110
|
vtx.c_bits := "000000011"; -- EEP = P110
|
end if;
|
end if;
|
else
|
else
|
-- prepare to send data char
|
-- prepare to send data char
|
vtx.c_bits := rtx.b_token.char & '0';
|
vtx.c_bits := rtx.b_token.char & '0';
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
-- Stage D: Prepare to transmit FCT, ESC, or the stuff from stage C.
|
-- Stage D: Prepare to transmit FCT, ESC, or the stuff from stage C.
|
if rtx.c_esc = '1' then
|
if rtx.c_esc = '1' then
|
-- prepare to send ESC
|
-- prepare to send ESC
|
vtx.d_bits := "000000111"; -- ESC = P111
|
vtx.d_bits := "000000111"; -- ESC = P111
|
vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit
|
vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit
|
vtx.d_cnt10 := '0';
|
vtx.d_cnt10 := '0';
|
elsif rtx.c_fct = '1' then
|
elsif rtx.c_fct = '1' then
|
-- prepare to send FCT
|
-- prepare to send FCT
|
vtx.d_bits := "000000001"; -- FCT = P100
|
vtx.d_bits := "000000001"; -- FCT = P100
|
vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit
|
vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit
|
vtx.d_cnt10 := '0';
|
vtx.d_cnt10 := '0';
|
else
|
else
|
-- send the stuff from stage C.
|
-- send the stuff from stage C.
|
vtx.d_bits := rtx.c_bits;
|
vtx.d_bits := rtx.c_bits;
|
vtx.d_cnt4 := rtx.c_bits(0);
|
vtx.d_cnt4 := rtx.c_bits(0);
|
vtx.d_cnt10 := not rtx.c_bits(0);
|
vtx.d_cnt10 := not rtx.c_bits(0);
|
end if;
|
end if;
|
|
|
-- Stage E: Shift register.
|
-- Stage E: Shift register.
|
if rtx.txclken = '1' then
|
if rtx.txclken = '1' then
|
if rtx.e_count(0) = '1' then
|
if rtx.e_count(0) = '1' then
|
-- reload shift register; output parity bit
|
-- reload shift register; output parity bit
|
vtx.e_valid := '1';
|
vtx.e_valid := '1';
|
vtx.e_shift(vtx.e_shift'high downto 1) := rtx.d_bits;
|
vtx.e_shift(vtx.e_shift'high downto 1) := rtx.d_bits;
|
vtx.e_shift(0) := not (rtx.e_parity xor rtx.d_bits(0));
|
vtx.e_shift(0) := not (rtx.e_parity xor rtx.d_bits(0));
|
vtx.e_count := rtx.d_cnt10 & "00000" & rtx.d_cnt4 & "000";
|
vtx.e_count := rtx.d_cnt10 & "00000" & rtx.d_cnt4 & "000";
|
vtx.e_parity := rtx.d_bits(0);
|
vtx.e_parity := rtx.d_bits(0);
|
else
|
else
|
-- shift bits to output; update parity bit
|
-- shift bits to output; update parity bit
|
vtx.e_shift := '0' & rtx.e_shift(rtx.e_shift'high downto 1);
|
vtx.e_shift := '0' & rtx.e_shift(rtx.e_shift'high downto 1);
|
vtx.e_count := '0' & rtx.e_count(rtx.e_count'high downto 1);
|
vtx.e_count := '0' & rtx.e_count(rtx.e_count'high downto 1);
|
vtx.e_parity := rtx.e_parity xor rtx.e_shift(1);
|
vtx.e_parity := rtx.e_parity xor rtx.e_shift(1);
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
-- Stage F: Data/strobe encoding.
|
-- Stage F: Data/strobe encoding.
|
if rtx.txclken = '1' then
|
if rtx.txclken = '1' then
|
if rtx.e_valid = '1' then
|
if rtx.e_valid = '1' then
|
-- output next data/strobe bits
|
-- output next data/strobe bits
|
vtx.f_spwdo := rtx.e_shift(0);
|
vtx.f_spwdo := rtx.e_shift(0);
|
vtx.f_spwso := not (rtx.e_shift(0) xor rtx.f_spwdo xor rtx.f_spwso);
|
vtx.f_spwso := not (rtx.e_shift(0) xor rtx.f_spwdo xor rtx.f_spwso);
|
else
|
else
|
-- gentle reset of spacewire signals
|
-- gentle reset of spacewire signals
|
vtx.f_spwdo := rtx.f_spwdo and rtx.f_spwso;
|
vtx.f_spwdo := rtx.f_spwdo and rtx.f_spwso;
|
vtx.f_spwso := '0';
|
vtx.f_spwso := '0';
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
-- Generate tx clock enable
|
-- Generate tx clock enable
|
-- An 8-bit counter decrements on every clock. A txclken pulse is
|
-- An 8-bit counter decrements on every clock. A txclken pulse is
|
-- produced 2 cycles after the counter reaches value 2. Counter reload
|
-- produced 2 cycles after the counter reaches value 2. Counter reload
|
-- values of 0 and 1 are handled as special cases.
|
-- values of 0 and 1 are handled as special cases.
|
-- count down in blocks of two bits
|
-- count down in blocks of two bits
|
vtx.txclkcnt(1 downto 0) := std_logic_vector(unsigned(rtx.txclkcnt(1 downto 0)) - 1);
|
vtx.txclkcnt(1 downto 0) := std_logic_vector(unsigned(rtx.txclkcnt(1 downto 0)) - 1);
|
vtx.txclkcnt(3 downto 2) := std_logic_vector(unsigned(rtx.txclkcnt(3 downto 2)) - unsigned(rtx.txclkcy(0 downto 0)));
|
vtx.txclkcnt(3 downto 2) := std_logic_vector(unsigned(rtx.txclkcnt(3 downto 2)) - unsigned(rtx.txclkcy(0 downto 0)));
|
vtx.txclkcnt(5 downto 4) := std_logic_vector(unsigned(rtx.txclkcnt(5 downto 4)) - unsigned(rtx.txclkcy(1 downto 1)));
|
vtx.txclkcnt(5 downto 4) := std_logic_vector(unsigned(rtx.txclkcnt(5 downto 4)) - unsigned(rtx.txclkcy(1 downto 1)));
|
vtx.txclkcnt(7 downto 6) := std_logic_vector(unsigned(rtx.txclkcnt(7 downto 6)) - unsigned(rtx.txclkcy(2 downto 2)));
|
vtx.txclkcnt(7 downto 6) := std_logic_vector(unsigned(rtx.txclkcnt(7 downto 6)) - unsigned(rtx.txclkcy(2 downto 2)));
|
-- propagate carry in blocks of two bits
|
-- propagate carry in blocks of two bits
|
vtx.txclkcy(0) := bool_to_logic(rtx.txclkcnt(1 downto 0) = "00");
|
vtx.txclkcy(0) := bool_to_logic(rtx.txclkcnt(1 downto 0) = "00");
|
vtx.txclkcy(1) := rtx.txclkcy(0) and bool_to_logic(rtx.txclkcnt(3 downto 2) = "00");
|
vtx.txclkcy(1) := rtx.txclkcy(0) and bool_to_logic(rtx.txclkcnt(3 downto 2) = "00");
|
vtx.txclkcy(2) := rtx.txclkcy(1) and bool_to_logic(rtx.txclkcnt(5 downto 4) = "00");
|
vtx.txclkcy(2) := rtx.txclkcy(1) and bool_to_logic(rtx.txclkcnt(5 downto 4) = "00");
|
-- detect value 2 in counter
|
-- detect value 2 in counter
|
vtx.txclkdone(0) := bool_to_logic(rtx.txclkcnt(3 downto 0) = "0010");
|
vtx.txclkdone(0) := bool_to_logic(rtx.txclkcnt(3 downto 0) = "0010");
|
vtx.txclkdone(1) := bool_to_logic(rtx.txclkcnt(7 downto 4) = "0000");
|
vtx.txclkdone(1) := bool_to_logic(rtx.txclkcnt(7 downto 4) = "0000");
|
-- trigger txclken
|
-- trigger txclken
|
vtx.txclken := (rtx.txclkdone(0) and rtx.txclkdone(1)) or rtx.txclkpre;
|
vtx.txclken := (rtx.txclkdone(0) and rtx.txclkdone(1)) or rtx.txclkpre;
|
vtx.txclkpre := (not rtx.txdivnorm) and ((not rtx.txclkpre) or (not rtx.txclkdiv(0)));
|
vtx.txclkpre := (not rtx.txdivnorm) and ((not rtx.txclkpre) or (not rtx.txclkdiv(0)));
|
-- reload counter
|
-- reload counter
|
if rtx.txclken = '1' then
|
if rtx.txclken = '1' then
|
vtx.txclkcnt := rtx.txclkdiv;
|
vtx.txclkcnt := rtx.txclkdiv;
|
vtx.txclkcy := "000";
|
vtx.txclkcy := "000";
|
vtx.txclkdone := "00";
|
vtx.txclkdone := "00";
|
end if;
|
end if;
|
|
|
-- Synchronize txclkdiv
|
-- Synchronize txclkdiv
|
vtx.txdivsafe(0) := r.txdivsafe;
|
vtx.txdivsafe(0) := r.txdivsafe;
|
vtx.txdivsafe(1) := rtx.txdivsafe(0);
|
vtx.txdivsafe(1) := rtx.txdivsafe(0);
|
if rtx.txdivsafe(1) = '1' then
|
if rtx.txdivsafe(1) = '1' then
|
vtx.txclkdiv := r.txdivreg;
|
vtx.txclkdiv := r.txdivreg;
|
vtx.txdivnorm := r.txdivnorm;
|
vtx.txdivnorm := r.txdivnorm;
|
end if;
|
end if;
|
|
|
-- Synchronize txen signal.
|
-- Synchronize txen signal.
|
vtx.txensync(0) := r.txenreg;
|
vtx.txensync(0) := r.txenreg;
|
vtx.txensync(1) := rtx.txensync(0);
|
vtx.txensync(1) := rtx.txensync(0);
|
|
|
-- Transmitter disabled.
|
-- Transmitter disabled.
|
if rtx.txensync(1) = '0' then
|
if rtx.txensync(1) = '0' then
|
vtx.txflip0 := '0';
|
vtx.txflip0 := '0';
|
vtx.txflip1 := '0';
|
vtx.txflip1 := '0';
|
vtx.b_update := '0';
|
vtx.b_update := '0';
|
vtx.b_mux := '0';
|
vtx.b_mux := '0';
|
vtx.b_valid := '0';
|
vtx.b_valid := '0';
|
vtx.c_update := '0';
|
vtx.c_update := '0';
|
|
vtx.c_busy := '1';
|
vtx.c_esc := '1'; -- need to send 2nd part of NULL
|
vtx.c_esc := '1'; -- need to send 2nd part of NULL
|
vtx.c_fct := '1';
|
vtx.c_fct := '1';
|
vtx.d_bits := "000000111"; -- ESC = P111
|
vtx.d_bits := "000000111"; -- ESC = P111
|
vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit
|
vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit
|
vtx.d_cnt10 := '0';
|
vtx.d_cnt10 := '0';
|
vtx.e_valid := '0';
|
vtx.e_valid := '0';
|
vtx.e_parity := '0';
|
vtx.e_parity := '0';
|
vtx.e_count := (0 => '1', others => '0');
|
vtx.e_count := (0 => '1', others => '0');
|
end if;
|
end if;
|
|
|
-- Reset.
|
-- Reset.
|
if s_tx_reset = '1' then
|
if s_tx_reset = '1' then
|
vtx.f_spwdo := '0';
|
vtx.f_spwdo := '0';
|
vtx.f_spwso := '0';
|
vtx.f_spwso := '0';
|
vtx.txensync := "00";
|
vtx.txensync := "00";
|
vtx.txclken := '0';
|
vtx.txclken := '0';
|
vtx.txclkpre := '1';
|
vtx.txclkpre := '1';
|
vtx.txclkcnt := (others => '0');
|
vtx.txclkcnt := (others => '0');
|
vtx.txclkdiv := (others => '0');
|
vtx.txclkdiv := (others => '0');
|
vtx.txdivnorm := '0';
|
vtx.txdivnorm := '0';
|
end if;
|
end if;
|
|
|
-- ---- SYSTEM CLOCK DOMAIN ----
|
-- ---- SYSTEM CLOCK DOMAIN ----
|
|
|
-- Hold divcnt and txen for use by txclk domain.
|
-- Hold divcnt and txen for use by txclk domain.
|
v.txdivtmp := std_logic_vector(unsigned(r.txdivtmp) - 1);
|
v.txdivtmp := std_logic_vector(unsigned(r.txdivtmp) - 1);
|
if r.txdivtmp = "00" then
|
if r.txdivtmp = "00" then
|
if r.txdivsafe = '0' then
|
if r.txdivsafe = '0' then
|
-- Latch the current value of divcnt and txen.
|
-- Latch the current value of divcnt and txen.
|
v.txdivsafe := '1';
|
v.txdivsafe := '1';
|
v.txdivtmp := "01";
|
v.txdivtmp := "01";
|
v.txdivreg := divcnt;
|
v.txdivreg := divcnt;
|
if unsigned(divcnt(divcnt'high downto 1)) = 0 then
|
if unsigned(divcnt(divcnt'high downto 1)) = 0 then
|
v.txdivnorm := '0';
|
v.txdivnorm := '0';
|
else
|
else
|
v.txdivnorm := '1';
|
v.txdivnorm := '1';
|
end if;
|
end if;
|
v.txenreg := xmiti.txen;
|
v.txenreg := xmiti.txen;
|
else
|
else
|
-- Drop the txdivsafe flag but keep latched values.
|
-- Drop the txdivsafe flag but keep latched values.
|
v.txdivsafe := '0';
|
v.txdivsafe := '0';
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
-- Pass falling edge of txen signal as soon as possible.
|
-- Pass falling edge of txen signal as soon as possible.
|
if xmiti.txen = '0' then
|
if xmiti.txen = '0' then
|
v.txenreg := '0';
|
v.txenreg := '0';
|
end if;
|
end if;
|
|
|
-- Synchronize feedback from txclk domain.
|
-- Synchronize feedback from txclk domain.
|
v.txflip0(0) := rtx.txflip0;
|
v.txflip0(0) := rtx.txflip0;
|
v.txflip0(1) := r.txflip0(0);
|
v.txflip0(1) := r.txflip0(0);
|
v.txflip1(0) := rtx.txflip1;
|
v.txflip1(0) := rtx.txflip1;
|
v.txflip1(1) := r.txflip1(0);
|
v.txflip1(1) := r.txflip1(0);
|
|
|
-- Store requests for FCT transmission.
|
-- Store requests for FCT transmission.
|
if xmiti.fct_in = '1' and r.allow_fct = '1' then
|
if xmiti.fct_in = '1' and r.allow_fct = '1' then
|
v.pend_fct := '1';
|
v.pend_fct := '1';
|
end if;
|
end if;
|
|
|
if xmiti.txen = '0' then
|
if xmiti.txen = '0' then
|
|
|
-- Transmitter disabled; reset state.
|
-- Transmitter disabled; reset state.
|
v.sysflip0 := '0';
|
v.sysflip0 := '0';
|
v.sysflip1 := '0';
|
v.sysflip1 := '0';
|
v.tokmux := '0';
|
v.tokmux := '0';
|
v.pend_fct := '0';
|
v.pend_fct := '0';
|
v.pend_char := '0';
|
v.pend_char := '0';
|
v.pend_tick := '0';
|
v.pend_tick := '0';
|
v.allow_fct := '0';
|
v.allow_fct := '0';
|
v.allow_char := '0';
|
v.allow_char := '0';
|
v.sent_fct := '0';
|
v.sent_fct := '0';
|
|
|
else
|
else
|
|
|
-- Determine if a new token is needed.
|
-- Determine if a new token is needed.
|
if r.tokmux = '0' then
|
if r.tokmux = '0' then
|
if r.sysflip0 = r.txflip0(1) then
|
if r.sysflip0 = r.txflip0(1) then
|
v_needtoken := '1';
|
v_needtoken := '1';
|
end if;
|
end if;
|
else
|
else
|
if r.sysflip1 = r.txflip1(1) then
|
if r.sysflip1 = r.txflip1(1) then
|
v_needtoken := '1';
|
v_needtoken := '1';
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
-- Prepare new token.
|
-- Prepare new token.
|
if r.allow_char = '1' and r.pend_tick = '1' then
|
if r.allow_char = '1' and r.pend_tick = '1' then
|
-- prepare to send time code
|
-- prepare to send time code
|
v_token.tick := '1';
|
v_token.tick := '1';
|
v_token.fct := '0';
|
v_token.fct := '0';
|
|
v_token.fctpiggy := '0';
|
v_token.flag := '0';
|
v_token.flag := '0';
|
v_token.char := r.pend_time;
|
v_token.char := r.pend_time;
|
v_havetoken := '1';
|
v_havetoken := '1';
|
if v_needtoken = '1' then
|
if v_needtoken = '1' then
|
v.pend_tick := '0';
|
v.pend_tick := '0';
|
end if;
|
end if;
|
elsif r.allow_fct = '1' and (xmiti.fct_in = '1' or r.pend_fct = '1') then
|
else
|
|
if r.allow_fct = '1' and (xmiti.fct_in = '1' or r.pend_fct = '1') then
|
-- prepare to send FCT
|
-- prepare to send FCT
|
v_token.tick := '0';
|
|
v_token.fct := '1';
|
v_token.fct := '1';
|
v_token.flag := '0';
|
|
v_havetoken := '1';
|
v_havetoken := '1';
|
if v_needtoken = '1' then
|
if v_needtoken = '1' then
|
v.pend_fct := '0';
|
v.pend_fct := '0';
|
v.sent_fct := '1';
|
v.sent_fct := '1';
|
end if;
|
end if;
|
elsif r.allow_char = '1' and r.pend_char = '1' then
|
end if;
|
|
if r.allow_char = '1' and r.pend_char = '1' then
|
-- prepare to send N-Char
|
-- prepare to send N-Char
|
v_token.tick := '0';
|
-- Note: it is possible to send an FCT and an N-Char
|
v_token.fct := '0';
|
-- together by enabling the fctpiggy flag.
|
|
v_token.fctpiggy := v_token.fct;
|
v_token.flag := r.pend_data(8);
|
v_token.flag := r.pend_data(8);
|
v_token.char := r.pend_data(7 downto 0);
|
v_token.char := r.pend_data(7 downto 0);
|
v_havetoken := '1';
|
v_havetoken := '1';
|
if v_needtoken = '1' then
|
if v_needtoken = '1' then
|
v.pend_char := '0';
|
v.pend_char := '0';
|
end if;
|
end if;
|
|
end if;
|
end if;
|
end if;
|
|
|
-- Put new token in slot.
|
-- Put new token in slot.
|
if v_havetoken = '1' then
|
if v_havetoken = '1' then
|
if r.tokmux = '0' then
|
if r.tokmux = '0' then
|
if r.sysflip0 = r.txflip0(1) then
|
if r.sysflip0 = r.txflip0(1) then
|
v.sysflip0 := not r.sysflip0;
|
v.sysflip0 := not r.sysflip0;
|
v.token0 := v_token;
|
v.token0 := v_token;
|
v.tokmux := '1';
|
v.tokmux := '1';
|
end if;
|
end if;
|
else
|
else
|
if r.sysflip1 = r.txflip1(1) then
|
if r.sysflip1 = r.txflip1(1) then
|
v.sysflip1 := not r.sysflip1;
|
v.sysflip1 := not r.sysflip1;
|
v.token1 := v_token;
|
v.token1 := v_token;
|
v.tokmux := '0';
|
v.tokmux := '0';
|
end if;
|
end if;
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
-- Determine whether we are allowed to send FCTs and characters
|
-- Determine whether we are allowed to send FCTs and characters
|
v.allow_fct := not xmiti.stnull;
|
v.allow_fct := not xmiti.stnull;
|
v.allow_char := (not xmiti.stnull) and (not xmiti.stfct) and r.sent_fct;
|
v.allow_char := (not xmiti.stnull) and (not xmiti.stfct) and r.sent_fct;
|
|
|
-- Store request for data transmission.
|
-- Store request for data transmission.
|
if xmiti.txwrite = '1' and r.allow_char = '1' and r.pend_char = '0' then
|
if xmiti.txwrite = '1' and r.allow_char = '1' and r.pend_char = '0' then
|
v.pend_char := '1';
|
v.pend_char := '1';
|
v.pend_data := xmiti.txflag & xmiti.txdata;
|
v.pend_data := xmiti.txflag & xmiti.txdata;
|
end if;
|
end if;
|
|
|
-- Store requests for time tick transmission.
|
-- Store requests for time tick transmission.
|
if xmiti.tick_in = '1' then
|
if xmiti.tick_in = '1' then
|
v.pend_tick := '1';
|
v.pend_tick := '1';
|
v.pend_time := xmiti.ctrl_in & xmiti.time_in;
|
v.pend_time := xmiti.ctrl_in & xmiti.time_in;
|
end if;
|
end if;
|
|
|
end if;
|
end if;
|
|
|
-- Synchronous reset of system clock domain.
|
-- Synchronous reset of system clock domain.
|
if rst = '1' then
|
if rst = '1' then
|
v := regs_reset;
|
v := regs_reset;
|
end if;
|
end if;
|
|
|
-- Drive outputs.
|
-- Drive outputs.
|
-- Note: the outputs are combinatorially dependent on certain inputs.
|
-- Note: the outputs are combinatorially dependent on certain inputs.
|
|
|
-- Set fctack high if (FCT requested) and (FCTs allowed) AND
|
-- Set fctack high if (FCT requested) and (FCTs allowed) AND
|
-- (no FCT pending)
|
-- (no FCT pending)
|
xmito.fctack <= xmiti.fct_in and xmiti.txen and r.allow_fct and
|
xmito.fctack <= xmiti.fct_in and xmiti.txen and r.allow_fct and
|
(not r.pend_fct);
|
(not r.pend_fct);
|
|
|
-- Set txrdy high if (character requested) AND (characters allowed) AND
|
-- Set txrdy high if (character requested) AND (characters allowed) AND
|
-- (no character pending)
|
-- (no character pending)
|
xmito.txack <= xmiti.txwrite and xmiti.txen and r.allow_char and
|
xmito.txack <= xmiti.txwrite and xmiti.txen and r.allow_char and
|
(not r.pend_char);
|
(not r.pend_char);
|
|
|
-- Update registers.
|
-- Update registers.
|
rin <= v;
|
rin <= v;
|
rtxin <= vtx;
|
rtxin <= vtx;
|
end process;
|
end process;
|
|
|
-- Synchronous process in txclk domain
|
-- Synchronous process in txclk domain
|
process (txclk) is
|
process (txclk) is
|
begin
|
begin
|
if rising_edge(txclk) then
|
if rising_edge(txclk) then
|
-- drive spacewire output signals
|
-- drive spacewire output signals
|
s_spwdo <= rtx.f_spwdo;
|
s_spwdo <= rtx.f_spwdo;
|
s_spwso <= rtx.f_spwso;
|
s_spwso <= rtx.f_spwso;
|
-- update registers
|
-- update registers
|
rtx <= rtxin;
|
rtx <= rtxin;
|
end if;
|
end if;
|
end process;
|
end process;
|
|
|
-- Synchronous process in system clock domain
|
-- Synchronous process in system clock domain
|
process (clk) is
|
process (clk) is
|
begin
|
begin
|
if rising_edge(clk) then
|
if rising_edge(clk) then
|
-- update registers
|
-- update registers
|
r <= rin;
|
r <= rin;
|
end if;
|
end if;
|
end process;
|
end process;
|
|
|
-- Reset synchronizer for txclk domain
|
-- Reset synchronizer for txclk domain
|
process (txclk, rst) is
|
process (txclk, rst) is
|
begin
|
begin
|
if rst = '1' then
|
if rst = '1' then
|
s_tx_rst_sync <= "11";
|
s_tx_rst_sync <= "11";
|
s_tx_reset <= '1';
|
s_tx_reset <= '1';
|
elsif rising_edge(txclk) then
|
elsif rising_edge(txclk) then
|
s_tx_rst_sync <= s_tx_rst_sync(0 downto 0) & "0";
|
s_tx_rst_sync <= s_tx_rst_sync(0 downto 0) & "0";
|
s_tx_reset <= s_tx_rst_sync(1);
|
s_tx_reset <= s_tx_rst_sync(1);
|
end if;
|
end if;
|
end process;
|
end process;
|
|
|
end architecture spwxmit_fast_arch;
|
end architecture spwxmit_fast_arch;
|
|
|