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 |
7 |
jorisvr |
-- The data path through the txclk domain is divided into stages B through F
|
42 |
|
|
-- in a half-hearted attempt to keep things simple.
|
43 |
2 |
jorisvr |
--
|
44 |
|
|
-- Stage B takes a token from the FIFO buffer and updates a buffer status
|
45 |
|
|
-- flag to indicate that the buffer slot needs to be refilled. If the FIFO
|
46 |
|
|
-- is empty, a NULL is inserted. Stage B is triggered one clock after
|
47 |
|
|
-- stage E switches to a new token. If the previous token was ESC, stage B
|
48 |
|
|
-- skips a turn because stage C will already know what to do.
|
49 |
|
|
--
|
50 |
|
|
-- Stage C takes a token from stage B and translates it into a bit pattern.
|
51 |
|
|
-- Time codes and NULL tokens are broken into two separate tokens starting
|
52 |
|
|
-- with ESC. Stage C is triggered one clock after the shift buffer in
|
53 |
|
|
-- stage E drops to 3 tokens.
|
54 |
|
|
--
|
55 |
|
|
-- Stage D completes the task of translating tokens to bit patterns and
|
56 |
|
|
-- distinguishes between 10-bit and 4-bit tokens. It is not explicitly
|
57 |
|
|
-- triggered but simply follows stage C.
|
58 |
|
|
--
|
59 |
|
|
-- Stage E is the bit shift register. It shifts when "txclken" is high.
|
60 |
|
|
-- A one-hot counter keeps track of the number of bits remaining in
|
61 |
|
|
-- the register. When the register falls empty, it loads a new 10-bit or
|
62 |
|
|
-- 4-bit pattern as prepared by stage D. Stage E also computes parity.
|
63 |
|
|
--
|
64 |
|
|
-- Stage F performs data strobe encoding. When the transmitter is disabled,
|
65 |
|
|
-- the outputs of stage F fall to zero in a controlled way.
|
66 |
|
|
--
|
67 |
|
|
-- To generate the transmission bit clock, the txclk is divided by an
|
68 |
|
|
-- integer factor (divcnt+1) using an 8-bit down counter. The implementation
|
69 |
|
|
-- of this counter has become quite complicated in order to meet timing goals.
|
70 |
|
|
-- The counter consists of 4 blocks of two bits each (txclkcnt), with a
|
71 |
|
|
-- carry-save concept used between blocks (txclkcy). Detection of terminal
|
72 |
|
|
-- count (txclkdone) has a pipeline delay of two cycles. Therefore a separate
|
73 |
|
|
-- concept is used if the initial count is less than 2 (txdivnorm). This is
|
74 |
|
|
-- all glued together in the final assignment to txclken.
|
75 |
|
|
--
|
76 |
|
|
-- The initial count for txclk division (divcnt) comes from the system clock
|
77 |
|
|
-- domain and thus needs to be synchronized for use in the txclk domain.
|
78 |
|
|
-- To facilitate this, the system clock domain latches the value of divcnt
|
79 |
|
|
-- once every 6 sysclk cycles and sets a flag to indicate when the latched
|
80 |
|
|
-- value can safely be used by the txclk domain.
|
81 |
|
|
--
|
82 |
|
|
-- A tricky aspect of the design is the initial state of the txclk logic.
|
83 |
|
|
-- When the transmitter is enabled (txen goes high), the txclk logic starts
|
84 |
|
|
-- with the first ESC pattern already set up in stage D, and stage C ready
|
85 |
|
|
-- to produce the FCT part of the first NULL.
|
86 |
|
|
--
|
87 |
|
|
-- The following guidelines are used to get good timing for the txclk domain:
|
88 |
|
|
-- * The new value of a register depends on at most 4 inputs (single LUT),
|
89 |
|
|
-- or in a few cases on 5 inputs (two LUTs and F5MUX).
|
90 |
|
|
-- * Synchronous resets may be used, but only if the reset signal comes
|
91 |
|
|
-- directly from a register (no logic in set/reset path);
|
92 |
|
|
-- * Clock enables may be used, but only if the enable signal comes directly
|
93 |
|
|
-- from a register (no logic in clock enable path).
|
94 |
|
|
--
|
95 |
|
|
-- Synchronization issues
|
96 |
|
|
-- ----------------------
|
97 |
|
|
--
|
98 |
|
|
-- There is a two-slot FIFO buffer between the system and txclk domains.
|
99 |
|
|
-- After the txclk domain pulls a token from the buffer, the system clock
|
100 |
|
|
-- domain should ideally refill the buffer before the txclk domain again
|
101 |
|
|
-- tries to pull from the same buffer slot. If the refill occurs late,
|
102 |
|
|
-- the txclk domain needs to insert a NULL token which is inefficient
|
103 |
|
|
-- use of bandwidth.
|
104 |
|
|
--
|
105 |
|
|
-- Assuming the transmission consists of a stream of data characters,
|
106 |
|
|
-- 10 bits per character, there are exactly 2*10 bit periods between
|
107 |
|
|
-- successive reads from the same buffer slot by the txclk logic.
|
108 |
|
|
--
|
109 |
|
|
-- The time needed for the system clock logic to refill a buffer slot =
|
110 |
|
|
-- 1 txclk period (update of rtx.txflipN)
|
111 |
|
|
-- + 1 txclk period (routing delay between domains)
|
112 |
|
|
-- + 2 sysclk periods (synchronizer for txflipN)
|
113 |
|
|
-- + 1 sysclk period (refill buffer slot and update r.sysflipN)
|
114 |
|
|
-- + 1 txclk period (routing delay between domains)
|
115 |
|
|
-- + 2 txclk periods (synchronizer for sysflipN)
|
116 |
|
|
-- = 5 txclk periods + 3 sysclk periods
|
117 |
|
|
--
|
118 |
|
|
-- If for example txclk is 4 times as fast as sysclk, this amounts to
|
119 |
|
|
-- 5 txclk + 3 sysclk = 5 + 3*4 txclk = 17 txclk
|
120 |
|
|
-- is less than 20 bit periods even at maximum transmission rate, so
|
121 |
|
|
-- no problem there.
|
122 |
|
|
--
|
123 |
|
|
-- This is different when the data stream includes 4-bit tokens.
|
124 |
3 |
jorisvr |
-- See the manual for further comments.
|
125 |
2 |
jorisvr |
--
|
126 |
|
|
-- Implementation guidelines
|
127 |
|
|
-- -------------------------
|
128 |
|
|
--
|
129 |
|
|
-- To minimize clock skew, IOB flip-flops should be used to drive
|
130 |
|
|
-- spw_do and spw_so.
|
131 |
|
|
--
|
132 |
|
|
-- "txclk" must be at least as fast as the system clock;
|
133 |
|
|
-- "txclk" does not need to be phase-related to the system clock;
|
134 |
|
|
-- it is allowed for "txclk" to be equal to "clk".
|
135 |
|
|
--
|
136 |
|
|
-- The following timing constraints are needed:
|
137 |
|
|
-- * PERIOD constraint on the system clock;
|
138 |
|
|
-- * PERIOD constraint on "txclk";
|
139 |
|
|
-- * FROM-TO constraint from "txclk" to the system clock, equal to
|
140 |
|
|
-- one "txclk" period;
|
141 |
|
|
-- * FROM-TO constraint from the system clock to "txclk", equal to
|
142 |
|
|
-- one "txclk" period.
|
143 |
|
|
--
|
144 |
|
|
|
145 |
|
|
library ieee;
|
146 |
|
|
use ieee.std_logic_1164.all;
|
147 |
|
|
use ieee.numeric_std.all;
|
148 |
|
|
use work.spwpkg.all;
|
149 |
|
|
|
150 |
|
|
entity spwxmit_fast is
|
151 |
|
|
|
152 |
|
|
port (
|
153 |
|
|
-- System clock.
|
154 |
|
|
clk: in std_logic;
|
155 |
|
|
|
156 |
|
|
-- Transmit clock.
|
157 |
|
|
txclk: in std_logic;
|
158 |
|
|
|
159 |
|
|
-- Synchronous reset (active-high)
|
160 |
|
|
-- Used asynchronously by fast clock domain (must be glitch-free).
|
161 |
|
|
rst: in std_logic;
|
162 |
|
|
|
163 |
|
|
-- Scaling factor minus 1, used to scale the system clock into the
|
164 |
|
|
-- transmission bit rate. The system clock is divided by
|
165 |
|
|
-- (unsigned(divcnt) + 1). Changing this signal will immediately
|
166 |
|
|
-- change the transmission rate.
|
167 |
|
|
divcnt: in std_logic_vector(7 downto 0);
|
168 |
|
|
|
169 |
|
|
-- Input signals from spwlink.
|
170 |
|
|
xmiti: in spw_xmit_in_type;
|
171 |
|
|
|
172 |
|
|
-- Output signals to spwlink.
|
173 |
|
|
xmito: out spw_xmit_out_type;
|
174 |
|
|
|
175 |
|
|
-- Data Out signal to SpaceWire bus.
|
176 |
|
|
spw_do: out std_logic;
|
177 |
|
|
|
178 |
|
|
-- Strobe Out signal to SpaceWire bus.
|
179 |
|
|
spw_so: out std_logic
|
180 |
|
|
);
|
181 |
|
|
|
182 |
|
|
-- Turn off FSM extraction to avoid synchronization problems.
|
183 |
|
|
attribute FSM_EXTRACT: string;
|
184 |
|
|
attribute FSM_EXTRACT of spwxmit_fast: entity is "NO";
|
185 |
|
|
|
186 |
|
|
end entity spwxmit_fast;
|
187 |
|
|
|
188 |
|
|
architecture spwxmit_fast_arch of spwxmit_fast is
|
189 |
|
|
|
190 |
|
|
-- Convert boolean to std_logic.
|
191 |
|
|
type bool_to_logic_type is array(boolean) of std_ulogic;
|
192 |
|
|
constant bool_to_logic: bool_to_logic_type := (false => '0', true => '1');
|
193 |
|
|
|
194 |
|
|
-- Data records passed between clock domains.
|
195 |
|
|
type token_type is record
|
196 |
|
|
tick: std_ulogic; -- send time code
|
197 |
|
|
fct: std_ulogic; -- send FCT
|
198 |
3 |
jorisvr |
fctpiggy: std_ulogic; -- send FCT and N-char
|
199 |
2 |
jorisvr |
flag: std_ulogic; -- send EOP or EEP
|
200 |
|
|
char: std_logic_vector(7 downto 0); -- character or time code
|
201 |
|
|
end record;
|
202 |
|
|
|
203 |
|
|
-- Registers in txclk domain
|
204 |
|
|
type txregs_type is record
|
205 |
|
|
-- sync to system clock domain
|
206 |
|
|
txflip0: std_ulogic;
|
207 |
|
|
txflip1: std_ulogic;
|
208 |
|
|
-- stage B
|
209 |
|
|
b_update: std_ulogic;
|
210 |
|
|
b_mux: std_ulogic;
|
211 |
|
|
b_txflip: std_ulogic;
|
212 |
|
|
b_valid: std_ulogic;
|
213 |
|
|
b_token: token_type;
|
214 |
|
|
-- stage C
|
215 |
|
|
c_update: std_ulogic;
|
216 |
3 |
jorisvr |
c_busy: std_ulogic;
|
217 |
2 |
jorisvr |
c_esc: std_ulogic;
|
218 |
|
|
c_fct: std_ulogic;
|
219 |
|
|
c_bits: std_logic_vector(8 downto 0);
|
220 |
|
|
-- stage D
|
221 |
|
|
d_bits: std_logic_vector(8 downto 0);
|
222 |
|
|
d_cnt4: std_ulogic;
|
223 |
|
|
d_cnt10: std_ulogic;
|
224 |
|
|
-- stage E
|
225 |
|
|
e_valid: std_ulogic;
|
226 |
|
|
e_shift: std_logic_vector(9 downto 0);
|
227 |
|
|
e_count: std_logic_vector(9 downto 0);
|
228 |
|
|
e_parity: std_ulogic;
|
229 |
|
|
-- stage F
|
230 |
|
|
f_spwdo: std_ulogic;
|
231 |
|
|
f_spwso: std_ulogic;
|
232 |
|
|
-- tx clock enable logic
|
233 |
|
|
txclken: std_ulogic;
|
234 |
|
|
txclkpre: std_ulogic;
|
235 |
|
|
txclkcnt: std_logic_vector(7 downto 0);
|
236 |
|
|
txclkcy: std_logic_vector(2 downto 0);
|
237 |
|
|
txclkdone: std_logic_vector(1 downto 0);
|
238 |
|
|
txclkdiv: std_logic_vector(7 downto 0);
|
239 |
|
|
txdivnorm: std_ulogic;
|
240 |
|
|
end record;
|
241 |
|
|
|
242 |
|
|
-- Registers in system clock domain
|
243 |
|
|
type regs_type is record
|
244 |
|
|
-- sync status to txclk domain
|
245 |
|
|
txenreg: std_ulogic;
|
246 |
|
|
txdivreg: std_logic_vector(7 downto 0);
|
247 |
|
|
txdivnorm: std_ulogic;
|
248 |
|
|
txdivtmp: std_logic_vector(1 downto 0);
|
249 |
|
|
txdivsafe: std_ulogic;
|
250 |
|
|
-- data stream to txclk domain
|
251 |
|
|
sysflip0: std_ulogic;
|
252 |
|
|
sysflip1: std_ulogic;
|
253 |
|
|
token0: token_type;
|
254 |
|
|
token1: token_type;
|
255 |
|
|
tokmux: std_ulogic;
|
256 |
|
|
-- transmitter management
|
257 |
|
|
pend_fct: std_ulogic; -- '1' if an outgoing FCT is pending
|
258 |
|
|
pend_char: std_ulogic; -- '1' if an outgoing N-Char is pending
|
259 |
|
|
pend_data: std_logic_vector(8 downto 0); -- control flag and data bits of pending char
|
260 |
|
|
pend_tick: std_ulogic; -- '1' if an outgoing time tick is pending
|
261 |
|
|
pend_time: std_logic_vector(7 downto 0); -- data bits of pending time tick
|
262 |
|
|
allow_fct: std_ulogic; -- '1' when allowed to send FCTs
|
263 |
|
|
allow_char: std_ulogic; -- '1' when allowed to send data and time
|
264 |
|
|
sent_fct: std_ulogic; -- '1' when at least one FCT token was sent
|
265 |
|
|
end record;
|
266 |
|
|
|
267 |
|
|
-- Initial state of system clock domain
|
268 |
3 |
jorisvr |
constant token_reset: token_type := (
|
269 |
|
|
tick => '0',
|
270 |
|
|
fct => '0',
|
271 |
|
|
fctpiggy => '0',
|
272 |
|
|
flag => '0',
|
273 |
|
|
char => (others => '0') );
|
274 |
2 |
jorisvr |
constant regs_reset: regs_type := (
|
275 |
|
|
txenreg => '0',
|
276 |
|
|
txdivreg => (others => '0'),
|
277 |
|
|
txdivnorm => '0',
|
278 |
|
|
txdivtmp => "00",
|
279 |
|
|
txdivsafe => '0',
|
280 |
|
|
sysflip0 => '0',
|
281 |
|
|
sysflip1 => '0',
|
282 |
3 |
jorisvr |
token0 => token_reset,
|
283 |
|
|
token1 => token_reset,
|
284 |
2 |
jorisvr |
tokmux => '0',
|
285 |
|
|
pend_fct => '0',
|
286 |
|
|
pend_char => '0',
|
287 |
|
|
pend_data => (others => '0'),
|
288 |
|
|
pend_tick => '0',
|
289 |
|
|
pend_time => (others => '0'),
|
290 |
|
|
allow_fct => '0',
|
291 |
|
|
allow_char => '0',
|
292 |
|
|
sent_fct => '0' );
|
293 |
|
|
|
294 |
7 |
jorisvr |
-- Signals that are re-synchronized from system clock to txclk domain.
|
295 |
|
|
type synctx_type is record
|
296 |
|
|
rstn: std_ulogic;
|
297 |
|
|
sysflip0: std_ulogic;
|
298 |
|
|
sysflip1: std_ulogic;
|
299 |
|
|
txen: std_ulogic;
|
300 |
|
|
txdivsafe: std_ulogic;
|
301 |
|
|
end record;
|
302 |
|
|
|
303 |
|
|
-- Signals that are re-synchronized from txclk to system clock domain.
|
304 |
|
|
type syncsys_type is record
|
305 |
|
|
txflip0: std_ulogic;
|
306 |
|
|
txflip1: std_ulogic;
|
307 |
|
|
end record;
|
308 |
|
|
|
309 |
2 |
jorisvr |
-- Registers
|
310 |
|
|
signal rtx: txregs_type;
|
311 |
|
|
signal rtxin: txregs_type;
|
312 |
|
|
signal r: regs_type := regs_reset;
|
313 |
|
|
signal rin: regs_type;
|
314 |
|
|
|
315 |
7 |
jorisvr |
-- Synchronized signals after crossing clock domains.
|
316 |
|
|
signal synctx: synctx_type;
|
317 |
|
|
signal syncsys: syncsys_type;
|
318 |
2 |
jorisvr |
|
319 |
|
|
-- Output flip-flops
|
320 |
|
|
signal s_spwdo: std_logic;
|
321 |
|
|
signal s_spwso: std_logic;
|
322 |
|
|
|
323 |
|
|
-- Force use of IOB flip-flops
|
324 |
|
|
attribute IOB: string;
|
325 |
|
|
attribute IOB of s_spwdo: signal is "TRUE";
|
326 |
|
|
attribute IOB of s_spwso: signal is "TRUE";
|
327 |
|
|
|
328 |
|
|
begin
|
329 |
|
|
|
330 |
7 |
jorisvr |
-- Reset synchronizer for txclk domain.
|
331 |
|
|
synctx_rst: syncdff
|
332 |
|
|
port map ( clk => txclk, rst => rst, di => '1', do => synctx.rstn );
|
333 |
|
|
|
334 |
|
|
-- Synchronize signals from system clock domain to txclk domain.
|
335 |
|
|
synctx_sysflip0: syncdff
|
336 |
|
|
port map ( clk => txclk, rst => rst, di => r.sysflip0, do => synctx.sysflip0 );
|
337 |
|
|
synctx_sysflip1: syncdff
|
338 |
|
|
port map ( clk => txclk, rst => rst, di => r.sysflip1, do => synctx.sysflip1 );
|
339 |
|
|
synctx_txen: syncdff
|
340 |
|
|
port map ( clk => txclk, rst => rst, di => r.txenreg, do => synctx.txen );
|
341 |
|
|
synctx_txdivsafe: syncdff
|
342 |
|
|
port map ( clk => txclk, rst => rst, di => r.txdivsafe, do => synctx.txdivsafe );
|
343 |
|
|
|
344 |
|
|
-- Synchronize signals from txclk domain to system clock domain.
|
345 |
|
|
syncsys_txflip0: syncdff
|
346 |
|
|
port map ( clk => clk, rst => rst, di => rtx.txflip0, do => syncsys.txflip0 );
|
347 |
|
|
syncsys_txflip1: syncdff
|
348 |
|
|
port map ( clk => clk, rst => rst, di => rtx.txflip1, do => syncsys.txflip1 );
|
349 |
|
|
|
350 |
2 |
jorisvr |
-- Drive SpaceWire output signals
|
351 |
|
|
spw_do <= s_spwdo;
|
352 |
|
|
spw_so <= s_spwso;
|
353 |
|
|
|
354 |
|
|
-- Combinatorial process
|
355 |
7 |
jorisvr |
process (r, rtx, rst, divcnt, xmiti, synctx, syncsys) is
|
356 |
2 |
jorisvr |
variable v: regs_type;
|
357 |
|
|
variable vtx: txregs_type;
|
358 |
|
|
variable v_needtoken: std_ulogic;
|
359 |
|
|
variable v_havetoken: std_ulogic;
|
360 |
|
|
variable v_token: token_type;
|
361 |
|
|
begin
|
362 |
|
|
v := r;
|
363 |
|
|
vtx := rtx;
|
364 |
|
|
v_needtoken := '0';
|
365 |
|
|
v_havetoken := '0';
|
366 |
3 |
jorisvr |
v_token := token_reset;
|
367 |
2 |
jorisvr |
|
368 |
|
|
-- ---- FAST CLOCK DOMAIN ----
|
369 |
|
|
|
370 |
|
|
-- Stage B: Multiplex tokens from system clock domain.
|
371 |
|
|
-- Update stage B three bit periods after updating stage C
|
372 |
|
|
-- (i.e. in time for the next update of stage C).
|
373 |
3 |
jorisvr |
-- Do not update stage B if stage C is indicating that it needs to
|
374 |
|
|
-- send a second token to complete its task.
|
375 |
|
|
vtx.b_update := rtx.txclken and rtx.e_count(0) and (not rtx.c_busy);
|
376 |
2 |
jorisvr |
if rtx.b_mux = '0' then
|
377 |
|
|
vtx.b_txflip := rtx.txflip0;
|
378 |
|
|
else
|
379 |
|
|
vtx.b_txflip := rtx.txflip1;
|
380 |
|
|
end if;
|
381 |
|
|
if rtx.b_update = '1' then
|
382 |
|
|
if rtx.b_mux = '0' then
|
383 |
|
|
-- get token from slot 0
|
384 |
7 |
jorisvr |
vtx.b_valid := synctx.sysflip0 xor rtx.b_txflip;
|
385 |
2 |
jorisvr |
vtx.b_token := r.token0;
|
386 |
|
|
-- update mux flag if we got a valid token
|
387 |
7 |
jorisvr |
vtx.b_mux := synctx.sysflip0 xor rtx.b_txflip;
|
388 |
|
|
vtx.txflip0 := synctx.sysflip0;
|
389 |
2 |
jorisvr |
vtx.txflip1 := rtx.txflip1;
|
390 |
|
|
else
|
391 |
|
|
-- get token from slot 1
|
392 |
7 |
jorisvr |
vtx.b_valid := synctx.sysflip1 xor rtx.b_txflip;
|
393 |
2 |
jorisvr |
vtx.b_token := r.token1;
|
394 |
|
|
-- update mux flag if we got a valid token
|
395 |
7 |
jorisvr |
vtx.b_mux := not (synctx.sysflip1 xor rtx.b_txflip);
|
396 |
2 |
jorisvr |
vtx.txflip0 := rtx.txflip0;
|
397 |
7 |
jorisvr |
vtx.txflip1 := synctx.sysflip1;
|
398 |
2 |
jorisvr |
end if;
|
399 |
|
|
end if;
|
400 |
|
|
|
401 |
|
|
-- Stage C: Prepare to transmit EOP, EEP or a data character.
|
402 |
|
|
vtx.c_update := rtx.txclken and rtx.e_count(3);
|
403 |
|
|
if rtx.c_update = '1' then
|
404 |
3 |
jorisvr |
|
405 |
|
|
-- NULL is broken into two tokens: ESC + FCT.
|
406 |
|
|
-- Time-codes are broken into two tokens: ESC + char.
|
407 |
|
|
|
408 |
|
|
-- Enable c_esc on the first pass of a NULL or a time-code.
|
409 |
2 |
jorisvr |
vtx.c_esc := (rtx.b_token.tick or (not rtx.b_valid)) and
|
410 |
|
|
(not rtx.c_esc);
|
411 |
3 |
jorisvr |
|
412 |
|
|
-- Enable c_fct on the first pass of an FCT and on
|
413 |
|
|
-- the second pass of a NULL (also the first pass, but c_esc
|
414 |
|
|
-- is stronger than c_fct).
|
415 |
|
|
vtx.c_fct := (rtx.b_token.fct and (not rtx.c_busy)) or
|
416 |
|
|
(not rtx.b_valid);
|
417 |
|
|
|
418 |
|
|
-- Enable c_busy on the first pass of a NULL or a time-code
|
419 |
|
|
-- or a piggy-backed FCT. This will tell stage B that we are
|
420 |
|
|
-- not done yet.
|
421 |
|
|
vtx.c_busy := (rtx.b_token.tick or (not rtx.b_valid) or
|
422 |
|
|
rtx.b_token.fctpiggy) and (not rtx.c_busy);
|
423 |
|
|
|
424 |
2 |
jorisvr |
if rtx.b_token.flag = '1' then
|
425 |
|
|
if rtx.b_token.char(0) = '0' then
|
426 |
|
|
-- prepare to send EOP
|
427 |
|
|
vtx.c_bits := "000000101"; -- EOP = P101
|
428 |
|
|
else
|
429 |
|
|
-- prepare to send EEP
|
430 |
|
|
vtx.c_bits := "000000011"; -- EEP = P110
|
431 |
|
|
end if;
|
432 |
|
|
else
|
433 |
|
|
-- prepare to send data char
|
434 |
|
|
vtx.c_bits := rtx.b_token.char & '0';
|
435 |
|
|
end if;
|
436 |
|
|
end if;
|
437 |
|
|
|
438 |
|
|
-- Stage D: Prepare to transmit FCT, ESC, or the stuff from stage C.
|
439 |
|
|
if rtx.c_esc = '1' then
|
440 |
|
|
-- prepare to send ESC
|
441 |
|
|
vtx.d_bits := "000000111"; -- ESC = P111
|
442 |
|
|
vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit
|
443 |
|
|
vtx.d_cnt10 := '0';
|
444 |
|
|
elsif rtx.c_fct = '1' then
|
445 |
|
|
-- prepare to send FCT
|
446 |
|
|
vtx.d_bits := "000000001"; -- FCT = P100
|
447 |
|
|
vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit
|
448 |
|
|
vtx.d_cnt10 := '0';
|
449 |
|
|
else
|
450 |
|
|
-- send the stuff from stage C.
|
451 |
|
|
vtx.d_bits := rtx.c_bits;
|
452 |
|
|
vtx.d_cnt4 := rtx.c_bits(0);
|
453 |
|
|
vtx.d_cnt10 := not rtx.c_bits(0);
|
454 |
|
|
end if;
|
455 |
|
|
|
456 |
|
|
-- Stage E: Shift register.
|
457 |
|
|
if rtx.txclken = '1' then
|
458 |
|
|
if rtx.e_count(0) = '1' then
|
459 |
|
|
-- reload shift register; output parity bit
|
460 |
|
|
vtx.e_valid := '1';
|
461 |
|
|
vtx.e_shift(vtx.e_shift'high downto 1) := rtx.d_bits;
|
462 |
|
|
vtx.e_shift(0) := not (rtx.e_parity xor rtx.d_bits(0));
|
463 |
|
|
vtx.e_count := rtx.d_cnt10 & "00000" & rtx.d_cnt4 & "000";
|
464 |
|
|
vtx.e_parity := rtx.d_bits(0);
|
465 |
|
|
else
|
466 |
|
|
-- shift bits to output; update parity bit
|
467 |
|
|
vtx.e_shift := '0' & rtx.e_shift(rtx.e_shift'high downto 1);
|
468 |
|
|
vtx.e_count := '0' & rtx.e_count(rtx.e_count'high downto 1);
|
469 |
|
|
vtx.e_parity := rtx.e_parity xor rtx.e_shift(1);
|
470 |
|
|
end if;
|
471 |
|
|
end if;
|
472 |
|
|
|
473 |
|
|
-- Stage F: Data/strobe encoding.
|
474 |
|
|
if rtx.txclken = '1' then
|
475 |
|
|
if rtx.e_valid = '1' then
|
476 |
|
|
-- output next data/strobe bits
|
477 |
|
|
vtx.f_spwdo := rtx.e_shift(0);
|
478 |
|
|
vtx.f_spwso := not (rtx.e_shift(0) xor rtx.f_spwdo xor rtx.f_spwso);
|
479 |
|
|
else
|
480 |
|
|
-- gentle reset of spacewire signals
|
481 |
|
|
vtx.f_spwdo := rtx.f_spwdo and rtx.f_spwso;
|
482 |
|
|
vtx.f_spwso := '0';
|
483 |
|
|
end if;
|
484 |
|
|
end if;
|
485 |
|
|
|
486 |
|
|
-- Generate tx clock enable
|
487 |
|
|
-- An 8-bit counter decrements on every clock. A txclken pulse is
|
488 |
|
|
-- produced 2 cycles after the counter reaches value 2. Counter reload
|
489 |
|
|
-- values of 0 and 1 are handled as special cases.
|
490 |
|
|
-- count down in blocks of two bits
|
491 |
|
|
vtx.txclkcnt(1 downto 0) := std_logic_vector(unsigned(rtx.txclkcnt(1 downto 0)) - 1);
|
492 |
|
|
vtx.txclkcnt(3 downto 2) := std_logic_vector(unsigned(rtx.txclkcnt(3 downto 2)) - unsigned(rtx.txclkcy(0 downto 0)));
|
493 |
|
|
vtx.txclkcnt(5 downto 4) := std_logic_vector(unsigned(rtx.txclkcnt(5 downto 4)) - unsigned(rtx.txclkcy(1 downto 1)));
|
494 |
|
|
vtx.txclkcnt(7 downto 6) := std_logic_vector(unsigned(rtx.txclkcnt(7 downto 6)) - unsigned(rtx.txclkcy(2 downto 2)));
|
495 |
|
|
-- propagate carry in blocks of two bits
|
496 |
|
|
vtx.txclkcy(0) := bool_to_logic(rtx.txclkcnt(1 downto 0) = "00");
|
497 |
|
|
vtx.txclkcy(1) := rtx.txclkcy(0) and bool_to_logic(rtx.txclkcnt(3 downto 2) = "00");
|
498 |
|
|
vtx.txclkcy(2) := rtx.txclkcy(1) and bool_to_logic(rtx.txclkcnt(5 downto 4) = "00");
|
499 |
|
|
-- detect value 2 in counter
|
500 |
|
|
vtx.txclkdone(0) := bool_to_logic(rtx.txclkcnt(3 downto 0) = "0010");
|
501 |
|
|
vtx.txclkdone(1) := bool_to_logic(rtx.txclkcnt(7 downto 4) = "0000");
|
502 |
|
|
-- trigger txclken
|
503 |
|
|
vtx.txclken := (rtx.txclkdone(0) and rtx.txclkdone(1)) or rtx.txclkpre;
|
504 |
|
|
vtx.txclkpre := (not rtx.txdivnorm) and ((not rtx.txclkpre) or (not rtx.txclkdiv(0)));
|
505 |
|
|
-- reload counter
|
506 |
|
|
if rtx.txclken = '1' then
|
507 |
|
|
vtx.txclkcnt := rtx.txclkdiv;
|
508 |
|
|
vtx.txclkcy := "000";
|
509 |
|
|
vtx.txclkdone := "00";
|
510 |
|
|
end if;
|
511 |
|
|
|
512 |
|
|
-- Synchronize txclkdiv
|
513 |
7 |
jorisvr |
if synctx.txdivsafe = '1' then
|
514 |
2 |
jorisvr |
vtx.txclkdiv := r.txdivreg;
|
515 |
|
|
vtx.txdivnorm := r.txdivnorm;
|
516 |
|
|
end if;
|
517 |
|
|
|
518 |
|
|
-- Transmitter disabled.
|
519 |
7 |
jorisvr |
if synctx.txen = '0' then
|
520 |
2 |
jorisvr |
vtx.txflip0 := '0';
|
521 |
|
|
vtx.txflip1 := '0';
|
522 |
|
|
vtx.b_update := '0';
|
523 |
|
|
vtx.b_mux := '0';
|
524 |
|
|
vtx.b_valid := '0';
|
525 |
|
|
vtx.c_update := '0';
|
526 |
3 |
jorisvr |
vtx.c_busy := '1';
|
527 |
2 |
jorisvr |
vtx.c_esc := '1'; -- need to send 2nd part of NULL
|
528 |
|
|
vtx.c_fct := '1';
|
529 |
|
|
vtx.d_bits := "000000111"; -- ESC = P111
|
530 |
|
|
vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit
|
531 |
|
|
vtx.d_cnt10 := '0';
|
532 |
|
|
vtx.e_valid := '0';
|
533 |
|
|
vtx.e_parity := '0';
|
534 |
|
|
vtx.e_count := (0 => '1', others => '0');
|
535 |
|
|
end if;
|
536 |
|
|
|
537 |
|
|
-- Reset.
|
538 |
7 |
jorisvr |
if synctx.rstn = '0' then
|
539 |
2 |
jorisvr |
vtx.f_spwdo := '0';
|
540 |
|
|
vtx.f_spwso := '0';
|
541 |
|
|
vtx.txclken := '0';
|
542 |
|
|
vtx.txclkpre := '1';
|
543 |
|
|
vtx.txclkcnt := (others => '0');
|
544 |
|
|
vtx.txclkdiv := (others => '0');
|
545 |
|
|
vtx.txdivnorm := '0';
|
546 |
|
|
end if;
|
547 |
|
|
|
548 |
|
|
-- ---- SYSTEM CLOCK DOMAIN ----
|
549 |
|
|
|
550 |
|
|
-- Hold divcnt and txen for use by txclk domain.
|
551 |
|
|
v.txdivtmp := std_logic_vector(unsigned(r.txdivtmp) - 1);
|
552 |
|
|
if r.txdivtmp = "00" then
|
553 |
|
|
if r.txdivsafe = '0' then
|
554 |
|
|
-- Latch the current value of divcnt and txen.
|
555 |
|
|
v.txdivsafe := '1';
|
556 |
|
|
v.txdivtmp := "01";
|
557 |
|
|
v.txdivreg := divcnt;
|
558 |
|
|
if unsigned(divcnt(divcnt'high downto 1)) = 0 then
|
559 |
|
|
v.txdivnorm := '0';
|
560 |
|
|
else
|
561 |
|
|
v.txdivnorm := '1';
|
562 |
|
|
end if;
|
563 |
|
|
v.txenreg := xmiti.txen;
|
564 |
|
|
else
|
565 |
|
|
-- Drop the txdivsafe flag but keep latched values.
|
566 |
|
|
v.txdivsafe := '0';
|
567 |
|
|
end if;
|
568 |
|
|
end if;
|
569 |
|
|
|
570 |
|
|
-- Pass falling edge of txen signal as soon as possible.
|
571 |
|
|
if xmiti.txen = '0' then
|
572 |
|
|
v.txenreg := '0';
|
573 |
|
|
end if;
|
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 |
7 |
jorisvr |
if r.sysflip0 = syncsys.txflip0 then
|
598 |
2 |
jorisvr |
v_needtoken := '1';
|
599 |
|
|
end if;
|
600 |
|
|
else
|
601 |
7 |
jorisvr |
if r.sysflip1 = syncsys.txflip1 then
|
602 |
2 |
jorisvr |
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 |
7 |
jorisvr |
if r.sysflip0 = syncsys.txflip0 then
|
646 |
2 |
jorisvr |
v.sysflip0 := not r.sysflip0;
|
647 |
|
|
v.token0 := v_token;
|
648 |
|
|
v.tokmux := '1';
|
649 |
|
|
end if;
|
650 |
|
|
else
|
651 |
7 |
jorisvr |
if r.sysflip1 = syncsys.txflip1 then
|
652 |
2 |
jorisvr |
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 |
|
|
end architecture spwxmit_fast_arch;
|