-- -- SpaceWire Transmitter -- -- This entity translates outgoing characters and tokens into -- data-strobe signalling. -- -- The output stage is driven by a separate transmission clock "txclk" which -- will typically be faster than the system clock. The actual transmission -- rate is determined by dividing the transmission clock by an integer factor. -- -- The code is tuned for implementation on Xilinx Spartan-3. -- -- Concept -- ------- -- -- Logic in the system clock domain generates a stream of tokens to be -- 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) -- 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 -- (r.sysflip0 and r.sysflip1) to indicate to the txclk domain that the -- buffer slot has been refilled. -- -- The txclk domain pulls tokens from the FIFO buffer, flipping flag -- 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 -- that a token has been consumed, it refills the buffer slot with a new -- token (assuming that there are tokens waiting to be transmitted). -- 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 -- system clock domain is late to refill the buffer. -- -- Details -- ------- -- -- Logic in the system clock domain accepts transmission requests through -- the external interface of the entity. Pending requests are translated -- into a stream of tokens. The tokens are pushed to the txclk domain through -- the FIFO buffer as described above. -- -- The data path through the txclk domain is divided into stages B through F -- in a half-hearted attempt to keep things simple. -- -- 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 -- 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 -- 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. -- 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 -- stage E drops to 3 tokens. -- -- Stage D completes the task of translating tokens to bit patterns and -- distinguishes between 10-bit and 4-bit tokens. It is not explicitly -- triggered but simply follows stage C. -- -- 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 -- 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. -- -- Stage F performs data strobe encoding. When the transmitter is disabled, -- the outputs of stage F fall to zero in a controlled way. -- -- To generate the transmission bit clock, the txclk is divided by an -- 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. -- The counter consists of 4 blocks of two bits each (txclkcnt), with a -- carry-save concept used between blocks (txclkcy). Detection of terminal -- 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 -- all glued together in the final assignment to txclken. -- -- 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. -- 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 -- value can safely be used by the txclk domain. -- -- 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 -- with the first ESC pattern already set up in stage D, and stage C ready -- to produce the FCT part of the first NULL. -- -- 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), -- or in a few cases on 5 inputs (two LUTs and F5MUX). -- * Synchronous resets may be used, but only if the reset signal comes -- directly from a register (no logic in set/reset path); -- * Clock enables may be used, but only if the enable signal comes directly -- from a register (no logic in clock enable path). -- -- Synchronization issues -- ---------------------- -- -- 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 -- domain should ideally refill the buffer before the txclk domain again -- 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 -- use of bandwidth. -- -- Assuming the transmission consists of a stream of data characters, -- 10 bits per character, there are exactly 2*10 bit periods between -- successive reads from the same buffer slot by the txclk logic. -- -- The time needed for the system clock logic to refill a buffer slot = -- 1 txclk period (update of rtx.txflipN) -- + 1 txclk period (routing delay between domains) -- + 2 sysclk periods (synchronizer for txflipN) -- + 1 sysclk period (refill buffer slot and update r.sysflipN) -- + 1 txclk period (routing delay between domains) -- + 2 txclk periods (synchronizer for sysflipN) -- = 5 txclk periods + 3 sysclk periods -- -- If for example txclk is 4 times as fast as sysclk, this amounts to -- 5 txclk + 3 sysclk = 5 + 3*4 txclk = 17 txclk -- is less than 20 bit periods even at maximum transmission rate, so -- no problem there. -- -- This is different when the data stream includes 4-bit tokens. -- See the manual for further comments. -- -- Implementation guidelines -- ------------------------- -- -- To minimize clock skew, IOB flip-flops should be used to drive -- spw_do and spw_so. -- -- "txclk" must be at least as fast as 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". -- -- The following timing constraints are needed: -- * PERIOD constraint on the system clock; -- * PERIOD constraint on "txclk"; -- * FROM-TO constraint from "txclk" to the system clock, equal to -- one "txclk" period; -- * FROM-TO constraint from the system clock to "txclk", equal to -- one "txclk" period. -- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.spwpkg.all; entity spwxmit_fast is port ( -- System clock. clk: in std_logic; -- Transmit clock. txclk: in std_logic; -- Synchronous reset (active-high) -- Used asynchronously by fast clock domain (must be glitch-free). rst: in std_logic; -- Scaling factor minus 1, used to scale the system clock into the -- transmission bit rate. The system clock is divided by -- (unsigned(divcnt) + 1). Changing this signal will immediately -- change the transmission rate. divcnt: in std_logic_vector(7 downto 0); -- Input signals from spwlink. xmiti: in spw_xmit_in_type; -- Output signals to spwlink. xmito: out spw_xmit_out_type; -- Data Out signal to SpaceWire bus. spw_do: out std_logic; -- Strobe Out signal to SpaceWire bus. spw_so: out std_logic ); -- Turn off FSM extraction to avoid synchronization problems. attribute FSM_EXTRACT: string; attribute FSM_EXTRACT of spwxmit_fast: entity is "NO"; end entity spwxmit_fast; architecture spwxmit_fast_arch of spwxmit_fast is -- Convert boolean to std_logic. type bool_to_logic_type is array(boolean) of std_ulogic; constant bool_to_logic: bool_to_logic_type := (false => '0', true => '1'); -- Data records passed between clock domains. type token_type is record tick: std_ulogic; -- send time code fct: std_ulogic; -- send FCT fctpiggy: std_ulogic; -- send FCT and N-char flag: std_ulogic; -- send EOP or EEP char: std_logic_vector(7 downto 0); -- character or time code end record; -- Registers in txclk domain type txregs_type is record -- sync to system clock domain txflip0: std_ulogic; txflip1: std_ulogic; -- stage B b_update: std_ulogic; b_mux: std_ulogic; b_txflip: std_ulogic; b_valid: std_ulogic; b_token: token_type; -- stage C c_update: std_ulogic; c_busy: std_ulogic; c_esc: std_ulogic; c_fct: std_ulogic; c_bits: std_logic_vector(8 downto 0); -- stage D d_bits: std_logic_vector(8 downto 0); d_cnt4: std_ulogic; d_cnt10: std_ulogic; -- stage E e_valid: std_ulogic; e_shift: std_logic_vector(9 downto 0); e_count: std_logic_vector(9 downto 0); e_parity: std_ulogic; -- stage F f_spwdo: std_ulogic; f_spwso: std_ulogic; -- tx clock enable logic txclken: std_ulogic; txclkpre: std_ulogic; txclkcnt: std_logic_vector(7 downto 0); txclkcy: std_logic_vector(2 downto 0); txclkdone: std_logic_vector(1 downto 0); txclkdiv: std_logic_vector(7 downto 0); txdivnorm: std_ulogic; end record; -- Registers in system clock domain type regs_type is record -- sync status to txclk domain txenreg: std_ulogic; txdivreg: std_logic_vector(7 downto 0); txdivnorm: std_ulogic; txdivtmp: std_logic_vector(1 downto 0); txdivsafe: std_ulogic; -- data stream to txclk domain sysflip0: std_ulogic; sysflip1: std_ulogic; token0: token_type; token1: token_type; tokmux: std_ulogic; -- transmitter management pend_fct: std_ulogic; -- '1' if an outgoing FCT 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_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 allow_fct: std_ulogic; -- '1' when allowed to send FCTs 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 end record; -- 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 := ( txenreg => '0', txdivreg => (others => '0'), txdivnorm => '0', txdivtmp => "00", txdivsafe => '0', sysflip0 => '0', sysflip1 => '0', token0 => token_reset, token1 => token_reset, tokmux => '0', pend_fct => '0', pend_char => '0', pend_data => (others => '0'), pend_tick => '0', pend_time => (others => '0'), allow_fct => '0', allow_char => '0', sent_fct => '0' ); -- Signals that are re-synchronized from system clock to txclk domain. type synctx_type is record rstn: std_ulogic; sysflip0: std_ulogic; sysflip1: std_ulogic; txen: std_ulogic; txdivsafe: std_ulogic; end record; -- Signals that are re-synchronized from txclk to system clock domain. type syncsys_type is record txflip0: std_ulogic; txflip1: std_ulogic; end record; -- Registers signal rtx: txregs_type; signal rtxin: txregs_type; signal r: regs_type := regs_reset; signal rin: regs_type; -- Synchronized signals after crossing clock domains. signal synctx: synctx_type; signal syncsys: syncsys_type; -- Output flip-flops signal s_spwdo: std_logic; signal s_spwso: std_logic; -- Force use of IOB flip-flops attribute IOB: string; attribute IOB of s_spwdo: signal is "TRUE"; attribute IOB of s_spwso: signal is "TRUE"; begin -- Reset synchronizer for txclk domain. synctx_rst: syncdff port map ( clk => txclk, rst => rst, di => '1', do => synctx.rstn ); -- Synchronize signals from system clock domain to txclk domain. synctx_sysflip0: syncdff port map ( clk => txclk, rst => rst, di => r.sysflip0, do => synctx.sysflip0 ); synctx_sysflip1: syncdff port map ( clk => txclk, rst => rst, di => r.sysflip1, do => synctx.sysflip1 ); synctx_txen: syncdff port map ( clk => txclk, rst => rst, di => r.txenreg, do => synctx.txen ); synctx_txdivsafe: syncdff port map ( clk => txclk, rst => rst, di => r.txdivsafe, do => synctx.txdivsafe ); -- Synchronize signals from txclk domain to system clock domain. syncsys_txflip0: syncdff port map ( clk => clk, rst => rst, di => rtx.txflip0, do => syncsys.txflip0 ); syncsys_txflip1: syncdff port map ( clk => clk, rst => rst, di => rtx.txflip1, do => syncsys.txflip1 ); -- Drive SpaceWire output signals spw_do <= s_spwdo; spw_so <= s_spwso; -- Combinatorial process process (r, rtx, rst, divcnt, xmiti, synctx, syncsys) is variable v: regs_type; variable vtx: txregs_type; variable v_needtoken: std_ulogic; variable v_havetoken: std_ulogic; variable v_token: token_type; begin v := r; vtx := rtx; v_needtoken := '0'; v_havetoken := '0'; v_token := token_reset; -- ---- FAST CLOCK DOMAIN ---- -- Stage B: Multiplex tokens from system clock domain. -- Update stage B three bit periods after updating stage C -- (i.e. in time for the next update of stage C). -- Do not update stage B if stage C is indicating that it needs to -- send a second token to complete its task. vtx.b_update := rtx.txclken and rtx.e_count(0) and (not rtx.c_busy); if rtx.b_mux = '0' then vtx.b_txflip := rtx.txflip0; else vtx.b_txflip := rtx.txflip1; end if; if rtx.b_update = '1' then if rtx.b_mux = '0' then -- get token from slot 0 vtx.b_valid := synctx.sysflip0 xor rtx.b_txflip; vtx.b_token := r.token0; -- update mux flag if we got a valid token vtx.b_mux := synctx.sysflip0 xor rtx.b_txflip; vtx.txflip0 := synctx.sysflip0; vtx.txflip1 := rtx.txflip1; else -- get token from slot 1 vtx.b_valid := synctx.sysflip1 xor rtx.b_txflip; vtx.b_token := r.token1; -- update mux flag if we got a valid token vtx.b_mux := not (synctx.sysflip1 xor rtx.b_txflip); vtx.txflip0 := rtx.txflip0; vtx.txflip1 := synctx.sysflip1; end if; end if; -- Stage C: Prepare to transmit EOP, EEP or a data character. vtx.c_update := rtx.txclken and rtx.e_count(3); if rtx.c_update = '1' then -- 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 (not rtx.c_esc); -- 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.char(0) = '0' then -- prepare to send EOP vtx.c_bits := "000000101"; -- EOP = P101 else -- prepare to send EEP vtx.c_bits := "000000011"; -- EEP = P110 end if; else -- prepare to send data char vtx.c_bits := rtx.b_token.char & '0'; end if; end if; -- Stage D: Prepare to transmit FCT, ESC, or the stuff from stage C. if rtx.c_esc = '1' then -- prepare to send ESC vtx.d_bits := "000000111"; -- ESC = P111 vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit vtx.d_cnt10 := '0'; elsif rtx.c_fct = '1' then -- prepare to send FCT vtx.d_bits := "000000001"; -- FCT = P100 vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit vtx.d_cnt10 := '0'; else -- send the stuff from stage C. vtx.d_bits := rtx.c_bits; vtx.d_cnt4 := rtx.c_bits(0); vtx.d_cnt10 := not rtx.c_bits(0); end if; -- Stage E: Shift register. if rtx.txclken = '1' then if rtx.e_count(0) = '1' then -- reload shift register; output parity bit vtx.e_valid := '1'; 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_count := rtx.d_cnt10 & "00000" & rtx.d_cnt4 & "000"; vtx.e_parity := rtx.d_bits(0); else -- shift bits to output; update parity bit 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_parity := rtx.e_parity xor rtx.e_shift(1); end if; end if; -- Stage F: Data/strobe encoding. if rtx.txclken = '1' then if rtx.e_valid = '1' then -- output next data/strobe bits vtx.f_spwdo := rtx.e_shift(0); vtx.f_spwso := not (rtx.e_shift(0) xor rtx.f_spwdo xor rtx.f_spwso); else -- gentle reset of spacewire signals vtx.f_spwdo := rtx.f_spwdo and rtx.f_spwso; vtx.f_spwso := '0'; end if; end if; -- Generate tx clock enable -- An 8-bit counter decrements on every clock. A txclken pulse is -- produced 2 cycles after the counter reaches value 2. Counter reload -- values of 0 and 1 are handled as special cases. -- count down in blocks of two bits 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(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))); -- propagate carry in blocks of two bits 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(2) := rtx.txclkcy(1) and bool_to_logic(rtx.txclkcnt(5 downto 4) = "00"); -- detect value 2 in counter vtx.txclkdone(0) := bool_to_logic(rtx.txclkcnt(3 downto 0) = "0010"); vtx.txclkdone(1) := bool_to_logic(rtx.txclkcnt(7 downto 4) = "0000"); -- trigger txclken 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))); -- reload counter if rtx.txclken = '1' then vtx.txclkcnt := rtx.txclkdiv; vtx.txclkcy := "000"; vtx.txclkdone := "00"; end if; -- Synchronize txclkdiv if synctx.txdivsafe = '1' then vtx.txclkdiv := r.txdivreg; vtx.txdivnorm := r.txdivnorm; end if; -- Transmitter disabled. if synctx.txen = '0' then vtx.txflip0 := '0'; vtx.txflip1 := '0'; vtx.b_update := '0'; vtx.b_mux := '0'; vtx.b_valid := '0'; vtx.c_update := '0'; vtx.c_busy := '1'; vtx.c_esc := '1'; -- need to send 2nd part of NULL vtx.c_fct := '1'; vtx.d_bits := "000000111"; -- ESC = P111 vtx.d_cnt4 := '1'; -- 3 bits + implicit parity bit vtx.d_cnt10 := '0'; vtx.e_valid := '0'; vtx.e_parity := '0'; vtx.e_count := (0 => '1', others => '0'); end if; -- Reset. if synctx.rstn = '0' then vtx.f_spwdo := '0'; vtx.f_spwso := '0'; vtx.txclken := '0'; vtx.txclkpre := '1'; vtx.txclkcnt := (others => '0'); vtx.txclkdiv := (others => '0'); vtx.txdivnorm := '0'; end if; -- ---- SYSTEM CLOCK DOMAIN ---- -- Hold divcnt and txen for use by txclk domain. v.txdivtmp := std_logic_vector(unsigned(r.txdivtmp) - 1); if r.txdivtmp = "00" then if r.txdivsafe = '0' then -- Latch the current value of divcnt and txen. v.txdivsafe := '1'; v.txdivtmp := "01"; v.txdivreg := divcnt; if unsigned(divcnt(divcnt'high downto 1)) = 0 then v.txdivnorm := '0'; else v.txdivnorm := '1'; end if; v.txenreg := xmiti.txen; else -- Drop the txdivsafe flag but keep latched values. v.txdivsafe := '0'; end if; end if; -- Pass falling edge of txen signal as soon as possible. if xmiti.txen = '0' then v.txenreg := '0'; end if; -- Store requests for FCT transmission. if xmiti.fct_in = '1' and r.allow_fct = '1' then v.pend_fct := '1'; end if; if xmiti.txen = '0' then -- Transmitter disabled; reset state. v.sysflip0 := '0'; v.sysflip1 := '0'; v.tokmux := '0'; v.pend_fct := '0'; v.pend_char := '0'; v.pend_tick := '0'; v.allow_fct := '0'; v.allow_char := '0'; v.sent_fct := '0'; else -- Determine if a new token is needed. if r.tokmux = '0' then if r.sysflip0 = syncsys.txflip0 then v_needtoken := '1'; end if; else if r.sysflip1 = syncsys.txflip1 then v_needtoken := '1'; end if; end if; -- Prepare new token. if r.allow_char = '1' and r.pend_tick = '1' then -- prepare to send time code v_token.tick := '1'; v_token.fct := '0'; v_token.fctpiggy := '0'; v_token.flag := '0'; v_token.char := r.pend_time; v_havetoken := '1'; if v_needtoken = '1' then v.pend_tick := '0'; end if; else if r.allow_fct = '1' and (xmiti.fct_in = '1' or r.pend_fct = '1') then -- prepare to send FCT v_token.fct := '1'; v_havetoken := '1'; if v_needtoken = '1' then v.pend_fct := '0'; v.sent_fct := '1'; end if; end if; if r.allow_char = '1' and r.pend_char = '1' then -- prepare to send N-Char -- Note: it is possible to send an FCT and an N-Char -- together by enabling the fctpiggy flag. v_token.fctpiggy := v_token.fct; v_token.flag := r.pend_data(8); v_token.char := r.pend_data(7 downto 0); v_havetoken := '1'; if v_needtoken = '1' then v.pend_char := '0'; end if; end if; end if; -- Put new token in slot. if v_havetoken = '1' then if r.tokmux = '0' then if r.sysflip0 = syncsys.txflip0 then v.sysflip0 := not r.sysflip0; v.token0 := v_token; v.tokmux := '1'; end if; else if r.sysflip1 = syncsys.txflip1 then v.sysflip1 := not r.sysflip1; v.token1 := v_token; v.tokmux := '0'; end if; end if; end if; -- Determine whether we are allowed to send FCTs and characters v.allow_fct := not xmiti.stnull; v.allow_char := (not xmiti.stnull) and (not xmiti.stfct) and r.sent_fct; -- Store request for data transmission. if xmiti.txwrite = '1' and r.allow_char = '1' and r.pend_char = '0' then v.pend_char := '1'; v.pend_data := xmiti.txflag & xmiti.txdata; end if; -- Store requests for time tick transmission. if xmiti.tick_in = '1' then v.pend_tick := '1'; v.pend_time := xmiti.ctrl_in & xmiti.time_in; end if; end if; -- Synchronous reset of system clock domain. if rst = '1' then v := regs_reset; end if; -- Drive outputs. -- Note: the outputs are combinatorially dependent on certain inputs. -- Set fctack high if (FCT requested) and (FCTs allowed) AND -- (no FCT pending) xmito.fctack <= xmiti.fct_in and xmiti.txen and r.allow_fct and (not r.pend_fct); -- Set txrdy high if (character requested) AND (characters allowed) AND -- (no character pending) xmito.txack <= xmiti.txwrite and xmiti.txen and r.allow_char and (not r.pend_char); -- Update registers. rin <= v; rtxin <= vtx; end process; -- Synchronous process in txclk domain process (txclk) is begin if rising_edge(txclk) then -- drive spacewire output signals s_spwdo <= rtx.f_spwdo; s_spwso <= rtx.f_spwso; -- update registers rtx <= rtxin; end if; end process; -- Synchronous process in system clock domain process (clk) is begin if rising_edge(clk) then -- update registers r <= rin; end if; end process; end architecture spwxmit_fast_arch;