| 1 |
2 |
jorisvr |
--
|
| 2 |
|
|
-- SpaceWire Transmitter
|
| 3 |
|
|
--
|
| 4 |
|
|
-- This entity translates outgoing characters and tokens into
|
| 5 |
|
|
-- data-strobe signalling.
|
| 6 |
|
|
--
|
| 7 |
|
|
-- The output stage is driven by a separate transmission clock "txclk" which
|
| 8 |
|
|
-- will typically be faster than the system clock. The actual transmission
|
| 9 |
|
|
-- rate is determined by dividing the transmission clock by an integer factor.
|
| 10 |
|
|
--
|
| 11 |
|
|
-- The code is tuned for implementation on Xilinx Spartan-3.
|
| 12 |
|
|
--
|
| 13 |
|
|
-- Concept
|
| 14 |
|
|
-- -------
|
| 15 |
|
|
--
|
| 16 |
|
|
-- Logic in the system clock domain generates a stream of tokens to be
|
| 17 |
|
|
-- transmitted. These tokens are encoded as instances of the token_type
|
| 18 |
|
|
-- record. Tokens are queued in a two-slot FIFO buffer (r.token0 and r.token1)
|
| 19 |
|
|
-- with a 1-bit pointer (r.tokmux) pointing to the head of the queue.
|
| 20 |
|
|
-- When a token is pushed into the buffer, a flag register is flipped
|
| 21 |
|
|
-- (r.sysflip0 and r.sysflip1) to indicate to the txclk domain that the
|
| 22 |
|
|
-- buffer slot has been refilled.
|
| 23 |
|
|
--
|
| 24 |
|
|
-- The txclk domain pulls tokens from the FIFO buffer, flipping flag
|
| 25 |
|
|
-- registers (rtx.txflip0 and rtx.txflip1) to indicate to the system clock
|
| 26 |
|
|
-- domain that a token has been pulled. When the system clock domain detects
|
| 27 |
|
|
-- that a token has been consumed, it refills the buffer slot with a new
|
| 28 |
|
|
-- token (assuming that there are tokens waiting to be transmitted).
|
| 29 |
|
|
-- Whenever the FIFO buffer is empty, the txclk domain sends NULLs instead.
|
| 30 |
|
|
-- This can happen either when there are no tokens to send, or when the
|
| 31 |
|
|
-- system clock domain is late to refill the buffer.
|
| 32 |
|
|
--
|
| 33 |
|
|
-- Details
|
| 34 |
|
|
-- -------
|
| 35 |
|
|
--
|
| 36 |
|
|
-- Logic in the system clock domain accepts transmission requests through
|
| 37 |
|
|
-- the external interface of the entity. Pending requests are translated
|
| 38 |
|
|
-- into a stream of tokens. The tokens are pushed to the txclk domain through
|
| 39 |
|
|
-- the FIFO buffer as described above.
|
| 40 |
|
|
--
|
| 41 |
|
|
-- The data path through the txclk domain is divided into stages A through F
|
| 42 |
|
|
-- in a half-hearted attempt to keep things simple. Stage A is just a
|
| 43 |
|
|
-- synchronizer for the buffer status flags from the system clock domain.
|
| 44 |
|
|
--
|
| 45 |
|
|
-- Stage B takes a token from the FIFO buffer and updates a buffer status
|
| 46 |
|
|
-- flag to indicate that the buffer slot needs to be refilled. If the FIFO
|
| 47 |
|
|
-- is empty, a NULL is inserted. Stage B is triggered one clock after
|
| 48 |
|
|
-- stage E switches to a new token. If the previous token was ESC, stage B
|
| 49 |
|
|
-- skips a turn because stage C will already know what to do.
|
| 50 |
|
|
--
|
| 51 |
|
|
-- Stage C takes a token from stage B and translates it into a bit pattern.
|
| 52 |
|
|
-- Time codes and NULL tokens are broken into two separate tokens starting
|
| 53 |
|
|
-- with ESC. Stage C is triggered one clock after the shift buffer in
|
| 54 |
|
|
-- stage E drops to 3 tokens.
|
| 55 |
|
|
--
|
| 56 |
|
|
-- Stage D completes the task of translating tokens to bit patterns and
|
| 57 |
|
|
-- distinguishes between 10-bit and 4-bit tokens. It is not explicitly
|
| 58 |
|
|
-- triggered but simply follows stage C.
|
| 59 |
|
|
--
|
| 60 |
|
|
-- Stage E is the bit shift register. It shifts when "txclken" is high.
|
| 61 |
|
|
-- A one-hot counter keeps track of the number of bits remaining in
|
| 62 |
|
|
-- the register. When the register falls empty, it loads a new 10-bit or
|
| 63 |
|
|
-- 4-bit pattern as prepared by stage D. Stage E also computes parity.
|
| 64 |
|
|
--
|
| 65 |
|
|
-- Stage F performs data strobe encoding. When the transmitter is disabled,
|
| 66 |
|
|
-- the outputs of stage F fall to zero in a controlled way.
|
| 67 |
|
|
--
|
| 68 |
|
|
-- To generate the transmission bit clock, the txclk is divided by an
|
| 69 |
|
|
-- integer factor (divcnt+1) using an 8-bit down counter. The implementation
|
| 70 |
|
|
-- of this counter has become quite complicated in order to meet timing goals.
|
| 71 |
|
|
-- The counter consists of 4 blocks of two bits each (txclkcnt), with a
|
| 72 |
|
|
-- carry-save concept used between blocks (txclkcy). Detection of terminal
|
| 73 |
|
|
-- count (txclkdone) has a pipeline delay of two cycles. Therefore a separate
|
| 74 |
|
|
-- concept is used if the initial count is less than 2 (txdivnorm). This is
|
| 75 |
|
|
-- all glued together in the final assignment to txclken.
|
| 76 |
|
|
--
|
| 77 |
|
|
-- The initial count for txclk division (divcnt) comes from the system clock
|
| 78 |
|
|
-- domain and thus needs to be synchronized for use in the txclk domain.
|
| 79 |
|
|
-- To facilitate this, the system clock domain latches the value of divcnt
|
| 80 |
|
|
-- once every 6 sysclk cycles and sets a flag to indicate when the latched
|
| 81 |
|
|
-- value can safely be used by the txclk domain.
|
| 82 |
|
|
--
|
| 83 |
|
|
-- A tricky aspect of the design is the initial state of the txclk logic.
|
| 84 |
|
|
-- When the transmitter is enabled (txen goes high), the txclk logic starts
|
| 85 |
|
|
-- with the first ESC pattern already set up in stage D, and stage C ready
|
| 86 |
|
|
-- to produce the FCT part of the first NULL.
|
| 87 |
|
|
--
|
| 88 |
|
|
-- The following guidelines are used to get good timing for the txclk domain:
|
| 89 |
|
|
-- * The new value of a register depends on at most 4 inputs (single LUT),
|
| 90 |
|
|
-- or in a few cases on 5 inputs (two LUTs and F5MUX).
|
| 91 |
|
|
-- * Synchronous resets may be used, but only if the reset signal comes
|
| 92 |
|
|
-- directly from a register (no logic in set/reset path);
|
| 93 |
|
|
-- * Clock enables may be used, but only if the enable signal comes directly
|
| 94 |
|
|
-- from a register (no logic in clock enable path).
|
| 95 |
|
|
--
|
| 96 |
|
|
-- Synchronization issues
|
| 97 |
|
|
-- ----------------------
|
| 98 |
|
|
--
|
| 99 |
|
|
-- There is a two-slot FIFO buffer between the system and txclk domains.
|
| 100 |
|
|
-- After the txclk domain pulls a token from the buffer, the system clock
|
| 101 |
|
|
-- domain should ideally refill the buffer before the txclk domain again
|
| 102 |
|
|
-- tries to pull from the same buffer slot. If the refill occurs late,
|
| 103 |
|
|
-- the txclk domain needs to insert a NULL token which is inefficient
|
| 104 |
|
|
-- use of bandwidth.
|
| 105 |
|
|
--
|
| 106 |
|
|
-- Assuming the transmission consists of a stream of data characters,
|
| 107 |
|
|
-- 10 bits per character, there are exactly 2*10 bit periods between
|
| 108 |
|
|
-- successive reads from the same buffer slot by the txclk logic.
|
| 109 |
|
|
--
|
| 110 |
|
|
-- The time needed for the system clock logic to refill a buffer slot =
|
| 111 |
|
|
-- 1 txclk period (update of rtx.txflipN)
|
| 112 |
|
|
-- + 1 txclk period (routing delay between domains)
|
| 113 |
|
|
-- + 2 sysclk periods (synchronizer for txflipN)
|
| 114 |
|
|
-- + 1 sysclk period (refill buffer slot and update r.sysflipN)
|
| 115 |
|
|
-- + 1 txclk period (routing delay between domains)
|
| 116 |
|
|
-- + 2 txclk periods (synchronizer for sysflipN)
|
| 117 |
|
|
-- = 5 txclk periods + 3 sysclk periods
|
| 118 |
|
|
--
|
| 119 |
|
|
-- If for example txclk is 4 times as fast as sysclk, this amounts to
|
| 120 |
|
|
-- 5 txclk + 3 sysclk = 5 + 3*4 txclk = 17 txclk
|
| 121 |
|
|
-- is less than 20 bit periods even at maximum transmission rate, so
|
| 122 |
|
|
-- no problem there.
|
| 123 |
|
|
--
|
| 124 |
|
|
-- This is different when the data stream includes 4-bit tokens.
|
| 125 |
3 |
jorisvr |
-- See the manual for further comments.
|
| 126 |
2 |
jorisvr |
--
|
| 127 |
|
|
-- Implementation guidelines
|
| 128 |
|
|
-- -------------------------
|
| 129 |
|
|
--
|
| 130 |
|
|
-- To minimize clock skew, IOB flip-flops should be used to drive
|
| 131 |
|
|
-- spw_do and spw_so.
|
| 132 |
|
|
--
|
| 133 |
|
|
-- "txclk" must be at least as fast as the system clock;
|
| 134 |
|
|
-- "txclk" does not need to be phase-related to the system clock;
|
| 135 |
|
|
-- it is allowed for "txclk" to be equal to "clk".
|
| 136 |
|
|
--
|
| 137 |
|
|
-- The following timing constraints are needed:
|
| 138 |
|
|
-- * PERIOD constraint on the system clock;
|
| 139 |
|
|
-- * PERIOD constraint on "txclk";
|
| 140 |
|
|
-- * FROM-TO constraint from "txclk" to the system clock, equal to
|
| 141 |
|
|
-- one "txclk" period;
|
| 142 |
|
|
-- * FROM-TO constraint from the system clock to "txclk", equal to
|
| 143 |
|
|
-- one "txclk" period.
|
| 144 |
|
|
--
|
| 145 |
|
|
|
| 146 |
|
|
library ieee;
|
| 147 |
|
|
use ieee.std_logic_1164.all;
|
| 148 |
|
|
use ieee.numeric_std.all;
|
| 149 |
|
|
use work.spwpkg.all;
|
| 150 |
|
|
|
| 151 |
|
|
entity spwxmit_fast is
|
| 152 |
|
|
|
| 153 |
|
|
port (
|
| 154 |
|
|
-- System clock.
|
| 155 |
|
|
clk: in std_logic;
|
| 156 |
|
|
|
| 157 |
|
|
-- Transmit clock.
|
| 158 |
|
|
txclk: in std_logic;
|
| 159 |
|
|
|
| 160 |
|
|
-- Synchronous reset (active-high)
|
| 161 |
|
|
-- Used asynchronously by fast clock domain (must be glitch-free).
|
| 162 |
|
|
rst: in std_logic;
|
| 163 |
|
|
|
| 164 |
|
|
-- Scaling factor minus 1, used to scale the system clock into the
|
| 165 |
|
|
-- transmission bit rate. The system clock is divided by
|
| 166 |
|
|
-- (unsigned(divcnt) + 1). Changing this signal will immediately
|
| 167 |
|
|
-- change the transmission rate.
|
| 168 |
|
|
divcnt: in std_logic_vector(7 downto 0);
|
| 169 |
|
|
|
| 170 |
|
|
-- Input signals from spwlink.
|
| 171 |
|
|
xmiti: in spw_xmit_in_type;
|
| 172 |
|
|
|
| 173 |
|
|
-- Output signals to spwlink.
|
| 174 |
|
|
xmito: out spw_xmit_out_type;
|
| 175 |
|
|
|
| 176 |
|
|
-- Data Out signal to SpaceWire bus.
|
| 177 |
|
|
spw_do: out std_logic;
|
| 178 |
|
|
|
| 179 |
|
|
-- Strobe Out signal to SpaceWire bus.
|
| 180 |
|
|
spw_so: out std_logic
|
| 181 |
|
|
);
|
| 182 |
|
|
|
| 183 |
|
|
-- Turn off FSM extraction to avoid synchronization problems.
|
| 184 |
|
|
attribute FSM_EXTRACT: string;
|
| 185 |
|
|
attribute FSM_EXTRACT of spwxmit_fast: entity is "NO";
|
| 186 |
|
|
|
| 187 |
|
|
-- Turn off register duplication to avoid synchronization problems.
|
| 188 |
|
|
attribute REGISTER_DUPLICATION: string;
|
| 189 |
|
|
attribute REGISTER_DUPLICATION of spwxmit_fast: entity is "FALSE";
|
| 190 |
|
|
|
| 191 |
|
|
end entity spwxmit_fast;
|
| 192 |
|
|
|
| 193 |
|
|
architecture spwxmit_fast_arch of spwxmit_fast is
|
| 194 |
|
|
|
| 195 |
|
|
-- Convert boolean to std_logic.
|
| 196 |
|
|
type bool_to_logic_type is array(boolean) of std_ulogic;
|
| 197 |
|
|
constant bool_to_logic: bool_to_logic_type := (false => '0', true => '1');
|
| 198 |
|
|
|
| 199 |
|
|
-- Data records passed between clock domains.
|
| 200 |
|
|
type token_type is record
|
| 201 |
|
|
tick: std_ulogic; -- send time code
|
| 202 |
|
|
fct: std_ulogic; -- send FCT
|
| 203 |
3 |
jorisvr |
fctpiggy: std_ulogic; -- send FCT and N-char
|
| 204 |
2 |
jorisvr |
flag: std_ulogic; -- send EOP or EEP
|
| 205 |
|
|
char: std_logic_vector(7 downto 0); -- character or time code
|
| 206 |
|
|
end record;
|
| 207 |
|
|
|
| 208 |
|
|
-- Registers in txclk domain
|
| 209 |
|
|
type txregs_type is record
|
| 210 |
|
|
-- sync to system clock domain
|
| 211 |
|
|
txflip0: std_ulogic;
|
| 212 |
|
|
txflip1: std_ulogic;
|
| 213 |
|
|
-- stage A
|
| 214 |
|
|
a_sysflip0: std_logic_vector(1 downto 0);
|
| 215 |
|
|
a_sysflip1: std_logic_vector(1 downto 0);
|
| 216 |
|
|
-- stage B
|
| 217 |
|
|
b_update: std_ulogic;
|
| 218 |
|
|
b_mux: std_ulogic;
|
| 219 |
|
|
b_txflip: std_ulogic;
|
| 220 |
|
|
b_valid: std_ulogic;
|
| 221 |
|
|
b_token: token_type;
|
| 222 |
|
|
-- stage C
|
| 223 |
|
|
c_update: std_ulogic;
|
| 224 |
3 |
jorisvr |
c_busy: std_ulogic;
|
| 225 |
2 |
jorisvr |
c_esc: std_ulogic;
|
| 226 |
|
|
c_fct: std_ulogic;
|
| 227 |
|
|
c_bits: std_logic_vector(8 downto 0);
|
| 228 |
|
|
-- stage D
|
| 229 |
|
|
d_bits: std_logic_vector(8 downto 0);
|
| 230 |
|
|
d_cnt4: std_ulogic;
|
| 231 |
|
|
d_cnt10: std_ulogic;
|
| 232 |
|
|
-- stage E
|
| 233 |
|
|
e_valid: std_ulogic;
|
| 234 |
|
|
e_shift: std_logic_vector(9 downto 0);
|
| 235 |
|
|
e_count: std_logic_vector(9 downto 0);
|
| 236 |
|
|
e_parity: std_ulogic;
|
| 237 |
|
|
-- stage F
|
| 238 |
|
|
f_spwdo: std_ulogic;
|
| 239 |
|
|
f_spwso: std_ulogic;
|
| 240 |
|
|
-- tx clock enable logic
|
| 241 |
|
|
txclken: std_ulogic;
|
| 242 |
|
|
txclkpre: std_ulogic;
|
| 243 |
|
|
txclkcnt: std_logic_vector(7 downto 0);
|
| 244 |
|
|
txclkcy: std_logic_vector(2 downto 0);
|
| 245 |
|
|
txclkdone: std_logic_vector(1 downto 0);
|
| 246 |
|
|
txclkdiv: std_logic_vector(7 downto 0);
|
| 247 |
|
|
txdivnorm: std_ulogic;
|
| 248 |
|
|
txdivsafe: std_logic_vector(1 downto 0);
|
| 249 |
|
|
-- tx enable logic
|
| 250 |
|
|
txensync: std_logic_vector(1 downto 0);
|
| 251 |
|
|
end record;
|
| 252 |
|
|
|
| 253 |
|
|
-- Registers in system clock domain
|
| 254 |
|
|
type regs_type is record
|
| 255 |
|
|
-- sync status to txclk domain
|
| 256 |
|
|
txenreg: std_ulogic;
|
| 257 |
|
|
txdivreg: std_logic_vector(7 downto 0);
|
| 258 |
|
|
txdivnorm: std_ulogic;
|
| 259 |
|
|
txdivtmp: std_logic_vector(1 downto 0);
|
| 260 |
|
|
txdivsafe: std_ulogic;
|
| 261 |
|
|
-- data stream to txclk domain
|
| 262 |
|
|
sysflip0: std_ulogic;
|
| 263 |
|
|
sysflip1: std_ulogic;
|
| 264 |
|
|
token0: token_type;
|
| 265 |
|
|
token1: token_type;
|
| 266 |
|
|
tokmux: std_ulogic;
|
| 267 |
|
|
-- sync feedback from txclk domain
|
| 268 |
|
|
txflip0: std_logic_vector(1 downto 0);
|
| 269 |
|
|
txflip1: std_logic_vector(1 downto 0);
|
| 270 |
|
|
-- transmitter management
|
| 271 |
|
|
pend_fct: std_ulogic; -- '1' if an outgoing FCT is pending
|
| 272 |
|
|
pend_char: std_ulogic; -- '1' if an outgoing N-Char is pending
|
| 273 |
|
|
pend_data: std_logic_vector(8 downto 0); -- control flag and data bits of pending char
|
| 274 |
|
|
pend_tick: std_ulogic; -- '1' if an outgoing time tick is pending
|
| 275 |
|
|
pend_time: std_logic_vector(7 downto 0); -- data bits of pending time tick
|
| 276 |
|
|
allow_fct: std_ulogic; -- '1' when allowed to send FCTs
|
| 277 |
|
|
allow_char: std_ulogic; -- '1' when allowed to send data and time
|
| 278 |
|
|
sent_fct: std_ulogic; -- '1' when at least one FCT token was sent
|
| 279 |
|
|
end record;
|
| 280 |
|
|
|
| 281 |
|
|
-- Initial state of system clock domain
|
| 282 |
3 |
jorisvr |
constant token_reset: token_type := (
|
| 283 |
|
|
tick => '0',
|
| 284 |
|
|
fct => '0',
|
| 285 |
|
|
fctpiggy => '0',
|
| 286 |
|
|
flag => '0',
|
| 287 |
|
|
char => (others => '0') );
|
| 288 |
2 |
jorisvr |
constant regs_reset: regs_type := (
|
| 289 |
|
|
txenreg => '0',
|
| 290 |
|
|
txdivreg => (others => '0'),
|
| 291 |
|
|
txdivnorm => '0',
|
| 292 |
|
|
txdivtmp => "00",
|
| 293 |
|
|
txdivsafe => '0',
|
| 294 |
|
|
sysflip0 => '0',
|
| 295 |
|
|
sysflip1 => '0',
|
| 296 |
3 |
jorisvr |
token0 => token_reset,
|
| 297 |
|
|
token1 => token_reset,
|
| 298 |
2 |
jorisvr |
tokmux => '0',
|
| 299 |
|
|
txflip0 => "00",
|
| 300 |
|
|
txflip1 => "00",
|
| 301 |
|
|
pend_fct => '0',
|
| 302 |
|
|
pend_char => '0',
|
| 303 |
|
|
pend_data => (others => '0'),
|
| 304 |
|
|
pend_tick => '0',
|
| 305 |
|
|
pend_time => (others => '0'),
|
| 306 |
|
|
allow_fct => '0',
|
| 307 |
|
|
allow_char => '0',
|
| 308 |
|
|
sent_fct => '0' );
|
| 309 |
|
|
|
| 310 |
|
|
-- Registers
|
| 311 |
|
|
signal rtx: txregs_type;
|
| 312 |
|
|
signal rtxin: txregs_type;
|
| 313 |
|
|
signal r: regs_type := regs_reset;
|
| 314 |
|
|
signal rin: regs_type;
|
| 315 |
|
|
|
| 316 |
|
|
-- Reset synchronizer for txclk domain
|
| 317 |
|
|
signal s_tx_rst_sync: std_logic_vector(1 downto 0) := "11";
|
| 318 |
|
|
signal s_tx_reset: std_ulogic := '1';
|
| 319 |
|
|
|
| 320 |
|
|
-- Output flip-flops
|
| 321 |
|
|
signal s_spwdo: std_logic;
|
| 322 |
|
|
signal s_spwso: std_logic;
|
| 323 |
|
|
|
| 324 |
|
|
-- Force use of IOB flip-flops
|
| 325 |
|
|
attribute IOB: string;
|
| 326 |
|
|
attribute IOB of s_spwdo: signal is "TRUE";
|
| 327 |
|
|
attribute IOB of s_spwso: signal is "TRUE";
|
| 328 |
|
|
|
| 329 |
|
|
begin
|
| 330 |
|
|
|
| 331 |
|
|
-- Drive SpaceWire output signals
|
| 332 |
|
|
spw_do <= s_spwdo;
|
| 333 |
|
|
spw_so <= s_spwso;
|
| 334 |
|
|
|
| 335 |
|
|
-- Combinatorial process
|
| 336 |
|
|
process (r, rtx, rst, divcnt, xmiti, s_tx_reset) is
|
| 337 |
|
|
variable v: regs_type;
|
| 338 |
|
|
variable vtx: txregs_type;
|
| 339 |
|
|
variable v_needtoken: std_ulogic;
|
| 340 |
|
|
variable v_havetoken: std_ulogic;
|
| 341 |
|
|
variable v_token: token_type;
|
| 342 |
|
|
begin
|
| 343 |
|
|
v := r;
|
| 344 |
|
|
vtx := rtx;
|
| 345 |
|
|
v_needtoken := '0';
|
| 346 |
|
|
v_havetoken := '0';
|
| 347 |
3 |
jorisvr |
v_token := token_reset;
|
| 348 |
2 |
jorisvr |
|
| 349 |
|
|
-- ---- FAST CLOCK DOMAIN ----
|
| 350 |
|
|
|
| 351 |
|
|
-- Stage A: Synchronize token buffer counts from system clock domain.
|
| 352 |
|
|
vtx.a_sysflip0(0) := r.sysflip0;
|
| 353 |
|
|
vtx.a_sysflip0(1) := rtx.a_sysflip0(0);
|
| 354 |
|
|
vtx.a_sysflip1(0) := r.sysflip1;
|
| 355 |
|
|
vtx.a_sysflip1(1) := rtx.a_sysflip1(0);
|
| 356 |
|
|
|
| 357 |
|
|
-- Stage B: Multiplex tokens from system clock domain.
|
| 358 |
|
|
-- Update stage B three bit periods after updating stage C
|
| 359 |
|
|
-- (i.e. in time for the next update of stage C).
|
| 360 |
3 |
jorisvr |
-- Do not update stage B if stage C is indicating that it needs to
|
| 361 |
|
|
-- send a second token to complete its task.
|
| 362 |
|
|
vtx.b_update := rtx.txclken and rtx.e_count(0) and (not rtx.c_busy);
|
| 363 |
2 |
jorisvr |
if rtx.b_mux = '0' then
|
| 364 |
|
|
vtx.b_txflip := rtx.txflip0;
|
| 365 |
|
|
else
|
| 366 |
|
|
vtx.b_txflip := rtx.txflip1;
|
| 367 |
|
|
end if;
|
| 368 |
|
|
if rtx.b_update = '1' then
|
| 369 |
|
|
if rtx.b_mux = '0' then
|
| 370 |
|
|
-- get token from slot 0
|
| 371 |
|
|
vtx.b_valid := rtx.a_sysflip0(1) xor rtx.b_txflip;
|
| 372 |
|
|
vtx.b_token := r.token0;
|
| 373 |
|
|
-- update mux flag if we got a valid token
|
| 374 |
|
|
vtx.b_mux := rtx.a_sysflip0(1) xor rtx.b_txflip;
|
| 375 |
|
|
vtx.txflip0 := rtx.a_sysflip0(1);
|
| 376 |
|
|
vtx.txflip1 := rtx.txflip1;
|
| 377 |
|
|
else
|
| 378 |
|
|
-- get token from slot 1
|
| 379 |
|
|
vtx.b_valid := rtx.a_sysflip1(1) xor rtx.b_txflip;
|
| 380 |
|
|
vtx.b_token := r.token1;
|
| 381 |
|
|
-- update mux flag if we got a valid token
|
| 382 |
|
|
vtx.b_mux := not (rtx.a_sysflip1(1) xor rtx.b_txflip);
|
| 383 |
|
|
vtx.txflip0 := rtx.txflip0;
|
| 384 |
|
|
vtx.txflip1 := rtx.a_sysflip1(1);
|
| 385 |
|
|
end if;
|
| 386 |
|
|
end if;
|
| 387 |
|
|
|
| 388 |
|
|
-- Stage C: Prepare to transmit EOP, EEP or a data character.
|
| 389 |
|
|
vtx.c_update := rtx.txclken and rtx.e_count(3);
|
| 390 |
|
|
if rtx.c_update = '1' then
|
| 391 |
3 |
jorisvr |
|
| 392 |
|
|
-- NULL is broken into two tokens: ESC + FCT.
|
| 393 |
|
|
-- Time-codes are broken into two tokens: ESC + char.
|
| 394 |
|
|
|
| 395 |
|
|
-- Enable c_esc on the first pass of a NULL or a time-code.
|
| 396 |
2 |
jorisvr |
vtx.c_esc := (rtx.b_token.tick or (not rtx.b_valid)) and
|
| 397 |
|
|
(not rtx.c_esc);
|
| 398 |
3 |
jorisvr |
|
| 399 |
|
|
-- Enable c_fct on the first pass of an FCT and on
|
| 400 |
|
|
-- the second pass of a NULL (also the first pass, but c_esc
|
| 401 |
|
|
-- is stronger than c_fct).
|
| 402 |
|
|
vtx.c_fct := (rtx.b_token.fct and (not rtx.c_busy)) or
|
| 403 |
|
|
(not rtx.b_valid);
|
| 404 |
|
|
|
| 405 |
|
|
-- Enable c_busy on the first pass of a NULL or a time-code
|
| 406 |
|
|
-- or a piggy-backed FCT. This will tell stage B that we are
|
| 407 |
|
|
-- not done yet.
|
| 408 |
|
|
vtx.c_busy := (rtx.b_token.tick or (not rtx.b_valid) or
|
| 409 |
|
|
rtx.b_token.fctpiggy) and (not rtx.c_busy);
|
| 410 |
|
|
|
| 411 |
2 |
jorisvr |
if rtx.b_token.flag = '1' then
|
| 412 |
|
|
if rtx.b_token.char(0) = '0' then
|
| 413 |
|
|
-- prepare to send EOP
|
| 414 |
|
|
vtx.c_bits := "000000101"; -- EOP = P101
|
| 415 |
|
|
else
|
| 416 |
|
|
-- prepare to send EEP
|
| 417 |
|
|
vtx.c_bits := "000000011"; -- EEP = P110
|
| 418 |
|
|
end if;
|
| 419 |
|
|
else
|
| 420 |
|
|
-- prepare to send data char
|
| 421 |
|
|
vtx.c_bits := rtx.b_token.char & '0';
|
| 422 |
|
|
end if;
|
| 423 |
|
|
end if;
|
| 424 |
|
|
|
| 425 |
|
|
-- Stage D: Prepare to transmit FCT, ESC, or the stuff from stage C.
|
| 426 |
|
|
if rtx.c_esc = '1' then
|
| 427 |
|
|
-- prepare to send ESC
|
| 428 |
|
|
vtx.d_bits := "000000111"; -- ESC = P111
|
| 429 |
|
|
vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit
|
| 430 |
|
|
vtx.d_cnt10 := '0';
|
| 431 |
|
|
elsif rtx.c_fct = '1' then
|
| 432 |
|
|
-- prepare to send FCT
|
| 433 |
|
|
vtx.d_bits := "000000001"; -- FCT = P100
|
| 434 |
|
|
vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit
|
| 435 |
|
|
vtx.d_cnt10 := '0';
|
| 436 |
|
|
else
|
| 437 |
|
|
-- send the stuff from stage C.
|
| 438 |
|
|
vtx.d_bits := rtx.c_bits;
|
| 439 |
|
|
vtx.d_cnt4 := rtx.c_bits(0);
|
| 440 |
|
|
vtx.d_cnt10 := not rtx.c_bits(0);
|
| 441 |
|
|
end if;
|
| 442 |
|
|
|
| 443 |
|
|
-- Stage E: Shift register.
|
| 444 |
|
|
if rtx.txclken = '1' then
|
| 445 |
|
|
if rtx.e_count(0) = '1' then
|
| 446 |
|
|
-- reload shift register; output parity bit
|
| 447 |
|
|
vtx.e_valid := '1';
|
| 448 |
|
|
vtx.e_shift(vtx.e_shift'high downto 1) := rtx.d_bits;
|
| 449 |
|
|
vtx.e_shift(0) := not (rtx.e_parity xor rtx.d_bits(0));
|
| 450 |
|
|
vtx.e_count := rtx.d_cnt10 & "00000" & rtx.d_cnt4 & "000";
|
| 451 |
|
|
vtx.e_parity := rtx.d_bits(0);
|
| 452 |
|
|
else
|
| 453 |
|
|
-- shift bits to output; update parity bit
|
| 454 |
|
|
vtx.e_shift := '0' & rtx.e_shift(rtx.e_shift'high downto 1);
|
| 455 |
|
|
vtx.e_count := '0' & rtx.e_count(rtx.e_count'high downto 1);
|
| 456 |
|
|
vtx.e_parity := rtx.e_parity xor rtx.e_shift(1);
|
| 457 |
|
|
end if;
|
| 458 |
|
|
end if;
|
| 459 |
|
|
|
| 460 |
|
|
-- Stage F: Data/strobe encoding.
|
| 461 |
|
|
if rtx.txclken = '1' then
|
| 462 |
|
|
if rtx.e_valid = '1' then
|
| 463 |
|
|
-- output next data/strobe bits
|
| 464 |
|
|
vtx.f_spwdo := rtx.e_shift(0);
|
| 465 |
|
|
vtx.f_spwso := not (rtx.e_shift(0) xor rtx.f_spwdo xor rtx.f_spwso);
|
| 466 |
|
|
else
|
| 467 |
|
|
-- gentle reset of spacewire signals
|
| 468 |
|
|
vtx.f_spwdo := rtx.f_spwdo and rtx.f_spwso;
|
| 469 |
|
|
vtx.f_spwso := '0';
|
| 470 |
|
|
end if;
|
| 471 |
|
|
end if;
|
| 472 |
|
|
|
| 473 |
|
|
-- Generate tx clock enable
|
| 474 |
|
|
-- An 8-bit counter decrements on every clock. A txclken pulse is
|
| 475 |
|
|
-- produced 2 cycles after the counter reaches value 2. Counter reload
|
| 476 |
|
|
-- values of 0 and 1 are handled as special cases.
|
| 477 |
|
|
-- count down in blocks of two bits
|
| 478 |
|
|
vtx.txclkcnt(1 downto 0) := std_logic_vector(unsigned(rtx.txclkcnt(1 downto 0)) - 1);
|
| 479 |
|
|
vtx.txclkcnt(3 downto 2) := std_logic_vector(unsigned(rtx.txclkcnt(3 downto 2)) - unsigned(rtx.txclkcy(0 downto 0)));
|
| 480 |
|
|
vtx.txclkcnt(5 downto 4) := std_logic_vector(unsigned(rtx.txclkcnt(5 downto 4)) - unsigned(rtx.txclkcy(1 downto 1)));
|
| 481 |
|
|
vtx.txclkcnt(7 downto 6) := std_logic_vector(unsigned(rtx.txclkcnt(7 downto 6)) - unsigned(rtx.txclkcy(2 downto 2)));
|
| 482 |
|
|
-- propagate carry in blocks of two bits
|
| 483 |
|
|
vtx.txclkcy(0) := bool_to_logic(rtx.txclkcnt(1 downto 0) = "00");
|
| 484 |
|
|
vtx.txclkcy(1) := rtx.txclkcy(0) and bool_to_logic(rtx.txclkcnt(3 downto 2) = "00");
|
| 485 |
|
|
vtx.txclkcy(2) := rtx.txclkcy(1) and bool_to_logic(rtx.txclkcnt(5 downto 4) = "00");
|
| 486 |
|
|
-- detect value 2 in counter
|
| 487 |
|
|
vtx.txclkdone(0) := bool_to_logic(rtx.txclkcnt(3 downto 0) = "0010");
|
| 488 |
|
|
vtx.txclkdone(1) := bool_to_logic(rtx.txclkcnt(7 downto 4) = "0000");
|
| 489 |
|
|
-- trigger txclken
|
| 490 |
|
|
vtx.txclken := (rtx.txclkdone(0) and rtx.txclkdone(1)) or rtx.txclkpre;
|
| 491 |
|
|
vtx.txclkpre := (not rtx.txdivnorm) and ((not rtx.txclkpre) or (not rtx.txclkdiv(0)));
|
| 492 |
|
|
-- reload counter
|
| 493 |
|
|
if rtx.txclken = '1' then
|
| 494 |
|
|
vtx.txclkcnt := rtx.txclkdiv;
|
| 495 |
|
|
vtx.txclkcy := "000";
|
| 496 |
|
|
vtx.txclkdone := "00";
|
| 497 |
|
|
end if;
|
| 498 |
|
|
|
| 499 |
|
|
-- Synchronize txclkdiv
|
| 500 |
|
|
vtx.txdivsafe(0) := r.txdivsafe;
|
| 501 |
|
|
vtx.txdivsafe(1) := rtx.txdivsafe(0);
|
| 502 |
|
|
if rtx.txdivsafe(1) = '1' then
|
| 503 |
|
|
vtx.txclkdiv := r.txdivreg;
|
| 504 |
|
|
vtx.txdivnorm := r.txdivnorm;
|
| 505 |
|
|
end if;
|
| 506 |
|
|
|
| 507 |
|
|
-- Synchronize txen signal.
|
| 508 |
|
|
vtx.txensync(0) := r.txenreg;
|
| 509 |
|
|
vtx.txensync(1) := rtx.txensync(0);
|
| 510 |
|
|
|
| 511 |
|
|
-- Transmitter disabled.
|
| 512 |
|
|
if rtx.txensync(1) = '0' then
|
| 513 |
|
|
vtx.txflip0 := '0';
|
| 514 |
|
|
vtx.txflip1 := '0';
|
| 515 |
|
|
vtx.b_update := '0';
|
| 516 |
|
|
vtx.b_mux := '0';
|
| 517 |
|
|
vtx.b_valid := '0';
|
| 518 |
|
|
vtx.c_update := '0';
|
| 519 |
3 |
jorisvr |
vtx.c_busy := '1';
|
| 520 |
2 |
jorisvr |
vtx.c_esc := '1'; -- need to send 2nd part of NULL
|
| 521 |
|
|
vtx.c_fct := '1';
|
| 522 |
|
|
vtx.d_bits := "000000111"; -- ESC = P111
|
| 523 |
|
|
vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit
|
| 524 |
|
|
vtx.d_cnt10 := '0';
|
| 525 |
|
|
vtx.e_valid := '0';
|
| 526 |
|
|
vtx.e_parity := '0';
|
| 527 |
|
|
vtx.e_count := (0 => '1', others => '0');
|
| 528 |
|
|
end if;
|
| 529 |
|
|
|
| 530 |
|
|
-- Reset.
|
| 531 |
|
|
if s_tx_reset = '1' then
|
| 532 |
|
|
vtx.f_spwdo := '0';
|
| 533 |
|
|
vtx.f_spwso := '0';
|
| 534 |
|
|
vtx.txensync := "00";
|
| 535 |
|
|
vtx.txclken := '0';
|
| 536 |
|
|
vtx.txclkpre := '1';
|
| 537 |
|
|
vtx.txclkcnt := (others => '0');
|
| 538 |
|
|
vtx.txclkdiv := (others => '0');
|
| 539 |
|
|
vtx.txdivnorm := '0';
|
| 540 |
|
|
end if;
|
| 541 |
|
|
|
| 542 |
|
|
-- ---- SYSTEM CLOCK DOMAIN ----
|
| 543 |
|
|
|
| 544 |
|
|
-- Hold divcnt and txen for use by txclk domain.
|
| 545 |
|
|
v.txdivtmp := std_logic_vector(unsigned(r.txdivtmp) - 1);
|
| 546 |
|
|
if r.txdivtmp = "00" then
|
| 547 |
|
|
if r.txdivsafe = '0' then
|
| 548 |
|
|
-- Latch the current value of divcnt and txen.
|
| 549 |
|
|
v.txdivsafe := '1';
|
| 550 |
|
|
v.txdivtmp := "01";
|
| 551 |
|
|
v.txdivreg := divcnt;
|
| 552 |
|
|
if unsigned(divcnt(divcnt'high downto 1)) = 0 then
|
| 553 |
|
|
v.txdivnorm := '0';
|
| 554 |
|
|
else
|
| 555 |
|
|
v.txdivnorm := '1';
|
| 556 |
|
|
end if;
|
| 557 |
|
|
v.txenreg := xmiti.txen;
|
| 558 |
|
|
else
|
| 559 |
|
|
-- Drop the txdivsafe flag but keep latched values.
|
| 560 |
|
|
v.txdivsafe := '0';
|
| 561 |
|
|
end if;
|
| 562 |
|
|
end if;
|
| 563 |
|
|
|
| 564 |
|
|
-- Pass falling edge of txen signal as soon as possible.
|
| 565 |
|
|
if xmiti.txen = '0' then
|
| 566 |
|
|
v.txenreg := '0';
|
| 567 |
|
|
end if;
|
| 568 |
|
|
|
| 569 |
|
|
-- Synchronize feedback from txclk domain.
|
| 570 |
|
|
v.txflip0(0) := rtx.txflip0;
|
| 571 |
|
|
v.txflip0(1) := r.txflip0(0);
|
| 572 |
|
|
v.txflip1(0) := rtx.txflip1;
|
| 573 |
|
|
v.txflip1(1) := r.txflip1(0);
|
| 574 |
|
|
|
| 575 |
|
|
-- Store requests for FCT transmission.
|
| 576 |
|
|
if xmiti.fct_in = '1' and r.allow_fct = '1' then
|
| 577 |
|
|
v.pend_fct := '1';
|
| 578 |
|
|
end if;
|
| 579 |
|
|
|
| 580 |
|
|
if xmiti.txen = '0' then
|
| 581 |
|
|
|
| 582 |
|
|
-- Transmitter disabled; reset state.
|
| 583 |
|
|
v.sysflip0 := '0';
|
| 584 |
|
|
v.sysflip1 := '0';
|
| 585 |
|
|
v.tokmux := '0';
|
| 586 |
|
|
v.pend_fct := '0';
|
| 587 |
|
|
v.pend_char := '0';
|
| 588 |
|
|
v.pend_tick := '0';
|
| 589 |
|
|
v.allow_fct := '0';
|
| 590 |
|
|
v.allow_char := '0';
|
| 591 |
|
|
v.sent_fct := '0';
|
| 592 |
|
|
|
| 593 |
|
|
else
|
| 594 |
|
|
|
| 595 |
|
|
-- Determine if a new token is needed.
|
| 596 |
|
|
if r.tokmux = '0' then
|
| 597 |
|
|
if r.sysflip0 = r.txflip0(1) then
|
| 598 |
|
|
v_needtoken := '1';
|
| 599 |
|
|
end if;
|
| 600 |
|
|
else
|
| 601 |
|
|
if r.sysflip1 = r.txflip1(1) then
|
| 602 |
|
|
v_needtoken := '1';
|
| 603 |
|
|
end if;
|
| 604 |
|
|
end if;
|
| 605 |
|
|
|
| 606 |
|
|
-- Prepare new token.
|
| 607 |
|
|
if r.allow_char = '1' and r.pend_tick = '1' then
|
| 608 |
|
|
-- prepare to send time code
|
| 609 |
|
|
v_token.tick := '1';
|
| 610 |
|
|
v_token.fct := '0';
|
| 611 |
3 |
jorisvr |
v_token.fctpiggy := '0';
|
| 612 |
2 |
jorisvr |
v_token.flag := '0';
|
| 613 |
|
|
v_token.char := r.pend_time;
|
| 614 |
|
|
v_havetoken := '1';
|
| 615 |
|
|
if v_needtoken = '1' then
|
| 616 |
|
|
v.pend_tick := '0';
|
| 617 |
|
|
end if;
|
| 618 |
3 |
jorisvr |
else
|
| 619 |
|
|
if r.allow_fct = '1' and (xmiti.fct_in = '1' or r.pend_fct = '1') then
|
| 620 |
|
|
-- prepare to send FCT
|
| 621 |
|
|
v_token.fct := '1';
|
| 622 |
|
|
v_havetoken := '1';
|
| 623 |
|
|
if v_needtoken = '1' then
|
| 624 |
|
|
v.pend_fct := '0';
|
| 625 |
|
|
v.sent_fct := '1';
|
| 626 |
|
|
end if;
|
| 627 |
2 |
jorisvr |
end if;
|
| 628 |
3 |
jorisvr |
if r.allow_char = '1' and r.pend_char = '1' then
|
| 629 |
|
|
-- prepare to send N-Char
|
| 630 |
|
|
-- Note: it is possible to send an FCT and an N-Char
|
| 631 |
|
|
-- together by enabling the fctpiggy flag.
|
| 632 |
|
|
v_token.fctpiggy := v_token.fct;
|
| 633 |
|
|
v_token.flag := r.pend_data(8);
|
| 634 |
|
|
v_token.char := r.pend_data(7 downto 0);
|
| 635 |
|
|
v_havetoken := '1';
|
| 636 |
|
|
if v_needtoken = '1' then
|
| 637 |
|
|
v.pend_char := '0';
|
| 638 |
|
|
end if;
|
| 639 |
2 |
jorisvr |
end if;
|
| 640 |
|
|
end if;
|
| 641 |
|
|
|
| 642 |
|
|
-- Put new token in slot.
|
| 643 |
|
|
if v_havetoken = '1' then
|
| 644 |
|
|
if r.tokmux = '0' then
|
| 645 |
|
|
if r.sysflip0 = r.txflip0(1) then
|
| 646 |
|
|
v.sysflip0 := not r.sysflip0;
|
| 647 |
|
|
v.token0 := v_token;
|
| 648 |
|
|
v.tokmux := '1';
|
| 649 |
|
|
end if;
|
| 650 |
|
|
else
|
| 651 |
|
|
if r.sysflip1 = r.txflip1(1) then
|
| 652 |
|
|
v.sysflip1 := not r.sysflip1;
|
| 653 |
|
|
v.token1 := v_token;
|
| 654 |
|
|
v.tokmux := '0';
|
| 655 |
|
|
end if;
|
| 656 |
|
|
end if;
|
| 657 |
|
|
end if;
|
| 658 |
|
|
|
| 659 |
|
|
-- Determine whether we are allowed to send FCTs and characters
|
| 660 |
|
|
v.allow_fct := not xmiti.stnull;
|
| 661 |
|
|
v.allow_char := (not xmiti.stnull) and (not xmiti.stfct) and r.sent_fct;
|
| 662 |
|
|
|
| 663 |
|
|
-- Store request for data transmission.
|
| 664 |
|
|
if xmiti.txwrite = '1' and r.allow_char = '1' and r.pend_char = '0' then
|
| 665 |
|
|
v.pend_char := '1';
|
| 666 |
|
|
v.pend_data := xmiti.txflag & xmiti.txdata;
|
| 667 |
|
|
end if;
|
| 668 |
|
|
|
| 669 |
|
|
-- Store requests for time tick transmission.
|
| 670 |
|
|
if xmiti.tick_in = '1' then
|
| 671 |
|
|
v.pend_tick := '1';
|
| 672 |
|
|
v.pend_time := xmiti.ctrl_in & xmiti.time_in;
|
| 673 |
|
|
end if;
|
| 674 |
|
|
|
| 675 |
|
|
end if;
|
| 676 |
|
|
|
| 677 |
|
|
-- Synchronous reset of system clock domain.
|
| 678 |
|
|
if rst = '1' then
|
| 679 |
|
|
v := regs_reset;
|
| 680 |
|
|
end if;
|
| 681 |
|
|
|
| 682 |
|
|
-- Drive outputs.
|
| 683 |
|
|
-- Note: the outputs are combinatorially dependent on certain inputs.
|
| 684 |
|
|
|
| 685 |
|
|
-- Set fctack high if (FCT requested) and (FCTs allowed) AND
|
| 686 |
|
|
-- (no FCT pending)
|
| 687 |
|
|
xmito.fctack <= xmiti.fct_in and xmiti.txen and r.allow_fct and
|
| 688 |
|
|
(not r.pend_fct);
|
| 689 |
|
|
|
| 690 |
|
|
-- Set txrdy high if (character requested) AND (characters allowed) AND
|
| 691 |
|
|
-- (no character pending)
|
| 692 |
|
|
xmito.txack <= xmiti.txwrite and xmiti.txen and r.allow_char and
|
| 693 |
|
|
(not r.pend_char);
|
| 694 |
|
|
|
| 695 |
|
|
-- Update registers.
|
| 696 |
|
|
rin <= v;
|
| 697 |
|
|
rtxin <= vtx;
|
| 698 |
|
|
end process;
|
| 699 |
|
|
|
| 700 |
|
|
-- Synchronous process in txclk domain
|
| 701 |
|
|
process (txclk) is
|
| 702 |
|
|
begin
|
| 703 |
|
|
if rising_edge(txclk) then
|
| 704 |
|
|
-- drive spacewire output signals
|
| 705 |
|
|
s_spwdo <= rtx.f_spwdo;
|
| 706 |
|
|
s_spwso <= rtx.f_spwso;
|
| 707 |
|
|
-- update registers
|
| 708 |
|
|
rtx <= rtxin;
|
| 709 |
|
|
end if;
|
| 710 |
|
|
end process;
|
| 711 |
|
|
|
| 712 |
|
|
-- Synchronous process in system clock domain
|
| 713 |
|
|
process (clk) is
|
| 714 |
|
|
begin
|
| 715 |
|
|
if rising_edge(clk) then
|
| 716 |
|
|
-- update registers
|
| 717 |
|
|
r <= rin;
|
| 718 |
|
|
end if;
|
| 719 |
|
|
end process;
|
| 720 |
|
|
|
| 721 |
|
|
-- Reset synchronizer for txclk domain
|
| 722 |
|
|
process (txclk, rst) is
|
| 723 |
|
|
begin
|
| 724 |
|
|
if rst = '1' then
|
| 725 |
|
|
s_tx_rst_sync <= "11";
|
| 726 |
|
|
s_tx_reset <= '1';
|
| 727 |
|
|
elsif rising_edge(txclk) then
|
| 728 |
|
|
s_tx_rst_sync <= s_tx_rst_sync(0 downto 0) & "0";
|
| 729 |
|
|
s_tx_reset <= s_tx_rst_sync(1);
|
| 730 |
|
|
end if;
|
| 731 |
|
|
end process;
|
| 732 |
|
|
|
| 733 |
|
|
end architecture spwxmit_fast_arch;
|