📄 a10281_ph.vhd
字号:
-- Drawing number : D10281-- Drawing description : Viterbi decoder core---- Entity name : d10281_ph-- Short description : Path history manager-- Architecture(s) : rtl---- Traceback memory management is a little tricky, because the simple approach-- requires 1 write and <traceback_length> reads per clock!-- The alternative method, known as Register Exchange, would require (for-- traceback depth T = 96) 6K flip-flops all going at full clock rate, that's-- 61 k gates.---- The method used here is based on a paper by Emmanuel Boutillon and -- Nicolas Demassieux. It utilises a small register exchange unit, of length-- equal to the memory order of the code (i.e. 6). That's just 384 flip-flops-- or 4k gates. This produces one 6-bit encoder state value every T clocks,-- which is used as the starting point to trace back for 96 output bits in a RAM-- bank. Meanwhile, a second RAM bank is being written with the new decisions.ARCHITECTURE rtl OF d10281_ph IS--==========================================-- Combinatorial --========================================== -- To mux the incoming memory buses, then select just one decision bit... SIGNAL muxed_mem_din : std_logic_vector( 2*trellis_width-1 DOWNTO 0 ); SIGNAL selected_memory_bit: std_logic; SIGNAL selected_y : std_logic; -- The decoded data! SIGNAL dout_int : std_logic; SIGNAL dout_y : std_logic; SIGNAL mem_write_address : traceback_address_type; SIGNAL mem_read_address : traceback_address_type;--==========================================-- Registers--========================================== -- Address counters are 1-bit bigger than traceback needs, because we have two banks. SUBTYPE address_counter_type IS unsigned(traceback_address_type'LENGTH DOWNTO 0); SIGNAL read_counter : address_counter_type; SIGNAL write_counter : address_counter_type; -- State machine for controlling the rather strange count sequence TYPE ctr_state_type IS (normal_ascending, normal_descending, short_ascending, offset_descending, offset_ascending, short_descending); SIGNAL ctr_state : ctr_state_type; -- Count the position within each block of traceback SIGNAL linear_counter : unsigned(traceback_address_type'RANGE); -- Count blocks of traceback at startup SIGNAL mask_counter : unsigned(1 DOWNTO 0); SIGNAL mask_counter_d1 : unsigned(1 DOWNTO 0); SIGNAL mask_counter_d2 : unsigned(1 DOWNTO 0); SIGNAL mask_counter_tre : unsigned(1 DOWNTO 0); SIGNAL mask_counter_d3 : unsigned(1 DOWNTO 0); -- values to compare or set the linear counter CONSTANT linear_counter_zero : unsigned(traceback_address_type'RANGE) := (OTHERS => '0'); CONSTANT linear_counter_max : unsigned(traceback_address_type'RANGE) := conv_unsigned(traceback_length-1, traceback_address_type'LENGTH); -- The extra memory location, and it's controls TYPE mem_type IS ARRAY ( 11 DOWNTO 0 ) OF std_logic_vector(2*trellis_width-1 DOWNTO 0); SIGNAL mem_2t : mem_type; SIGNAL write_mem_2t : BOOLEAN; SIGNAL read_mem_2t : BOOLEAN; -- The register exchange unit TYPE regex_type IS ARRAY ( trellis_width-1 DOWNTO 0 ) OF std_logic_vector(memory_order-1 DOWNTO 0); TYPE trel_regex_type IS ARRAY ( 11 DOWNTO 0 ) OF regex_type; SIGNAL regex : trel_regex_type; CONSTANT regex_zero : std_logic_vector(memory_order-1 DOWNTO 0) := (OTHERS => '0'); -- Whether the RegEx unit is in CAPTURE or SHUFFLE mode SIGNAL capture_regs : BOOLEAN; -- The traceback unit TYPE shift_type IS ARRAY ( 11 DOWNTO 0 ) OF unsigned(memory_order-1 DOWNTO 0); TYPE shift_left_type IS ARRAY ( 11 DOWNTO 0 ) OF BOOLEAN; SIGNAL shifter : shift_type; -- Bit reverser is like two back-to-back LIFO stacks TYPE rever_type IS ARRAY ( 11 DOWNTO 0 ) OF std_logic_vector(traceback_length-1 DOWNTO 0); SIGNAL reverser : rever_type; SIGNAL reverser_y : rever_type; SIGNAL reverser_shift_left : BOOLEAN; SIGNAL reverser_shift_d1 : BOOLEAN; SIGNAL tre_rd_index : std_logic_vector( 3 DOWNTO 0 ); SIGNAL tre_rd_d1 : std_logic_vector( 3 DOWNTO 0 ); SIGNAL rd_sel : std_logic; SIGNAL rd_sel_2t : BOOLEAN; SIGNAL trellis_en : std_logic; SIGNAL trellis_en_d1 : std_logic; SIGNAL shift_d : std_logic_vector( 11 DOWNTO 0 ); SIGNAL shift_y : std_logic_vector( 11 DOWNTO 0 ); BEGIN--===========================================================-- Combinatorial--=========================================================== -- Buffer these to the outside world (hold off data for first 3 blocks during startup) dovalid <= clk_out_enable WHEN mask_counter_d3 = 3 ELSE '0'; dout <= ( dout_y & dout_int ) WHEN mask_counter_d1 = 3 ELSE "00"; trellis_index <= tre_rd_index; -- DOUT comes from the bit reverser dout_int <= reverser(conv_integer(unsigned(tre_rd_index)))(traceback_length-1) WHEN reverser_shift_d1 ELSE reverser(conv_integer(unsigned(tre_rd_index)))(0); dout_y <= reverser_y(conv_integer(unsigned(tre_rd_index)))(traceback_length-1) WHEN reverser_shift_d1 ELSE reverser_y(conv_integer(unsigned(tre_rd_index)))(0); -- Select the appropriate memory, and then select just one decision bit muxed_mem_din <= mem_2t(conv_integer(unsigned(tre_rd_index))) WHEN rd_sel_2t ELSE mem2_din WHEN rd_sel = '1' ELSE mem1_din; selected_memory_bit <= muxed_mem_din(conv_integer(shifter(conv_integer(unsigned(tre_rd_index))))) AND mask_counter_d1(1); selected_y <= muxed_mem_din(conv_integer('1' & shifter(conv_integer(unsigned(tre_rd_index))))) AND mask_counter_d1(1); -- For the first two blocks (while reading uninitialised memory) gate off the -- decision data. -- memory controls: -- Split read/write counters into bank select (LSB) and address (the rest) mem_cs_n <= NOT clk_out_enable; mem_dout <= out_y & decision; mem_write_address <= std_logic_vector(write_counter(address_counter_type'HIGH DOWNTO 1)); mem_read_address <= std_logic_vector(read_counter(address_counter_type'HIGH DOWNTO 1)); mem1_a( traceback_addr_width-1 DOWNTO 0 ) <= mem_write_address WHEN write_counter(0) = '0' ELSE mem_read_address; mem2_a( traceback_addr_width-1 DOWNTO 0 ) <= mem_read_address WHEN write_counter(0) = '0' ELSE mem_write_address; mem1_a( traceback_addr_width+4-1 DOWNTO traceback_addr_width ) <= tre_rd_d1; mem2_a( traceback_addr_width+4-1 DOWNTO traceback_addr_width ) <= tre_rd_d1; mem1_we_n <= '1' WHEN write_mem_2t ELSE write_counter(0); mem2_we_n <= '1' WHEN write_mem_2t ELSE NOT write_counter(0); trellis_en <= '1' WHEN trellis_count = "0000" ELSE '0';--===========================================================-- Clocked--=========================================================== -- During each clock, we need to do one write and one read. -- In order to use ordinary RAM cells, the read and write need to be -- in different banks. To minimise storage, we read the byte we've just -- written. Thus the RAM banks are interleaved on the address LSB. -- Because each block of traceback_length T words needs to be read -- in reverse order with respect to how it was written, we actually need -- 2T + 1 words. The 2T'th word is a register. -- This mandates a rather strange count sequence! -- -- The counters have a range 0..2T-1. The LSB selects which bank to use. addrgen : PROCESS (clk_out, reset_n) -- To keep the following constant declarations readable, break out the -- conversions into a function: FUNCTION ac_val(n : NATURAL) RETURN address_counter_type IS BEGIN RETURN conv_unsigned(n, address_counter_type'LENGTH); END ac_val; -- constants with to compare and set the address counter: CONSTANT ac_0 : address_counter_type := ac_val(0); CONSTANT ac_1 : address_counter_type := ac_val(1); CONSTANT ac_t_minus_1 : address_counter_type := ac_val(traceback_length-1); CONSTANT ac_t : address_counter_type := ac_val(traceback_length); CONSTANT ac_t_plus_1 : address_counter_type := ac_val(traceback_length+1); CONSTANT ac_2t_minus_1 : address_counter_type := ac_val((2*traceback_length)-1); PROCEDURE modulo_inc(SIGNAL count : INOUT address_counter_type) IS BEGIN IF count = ac_2t_minus_1 THEN count <= ac_0; ELSE count <= count + 1; END IF; END modulo_inc; PROCEDURE modulo_dec(SIGNAL count : INOUT address_counter_type) IS BEGIN IF count = ac_0 THEN count <= ac_2t_minus_1; ELSE count <= count - 1; END IF; END modulo_dec; BEGIN IF reset_n = '0' THEN read_counter <= ac_1; write_counter <= ac_0; read_mem_2t <= FALSE; write_mem_2t <= FALSE; ctr_state <= normal_ascending; ELSIF clk_out'EVENT AND clk_out = '1' THEN IF viterbi_init = '1' THEN read_counter <= ac_1; write_counter <= ac_0; read_mem_2t <= FALSE; write_mem_2t <= FALSE; ctr_state <= normal_ascending; ELSIF clk_out_enable = '1' AND trellis_en = '1' THEN -- We always write the address we've just read, so write counter -- generation is easy: write_counter <= read_counter; write_mem_2t <= read_mem_2t; -- The read counter is rather more complex... IF read_mem_2t THEN -- Storing into the internal register (the 2T'th element) read_mem_2t <= FALSE; ELSE -- storing into RAM CASE ctr_state IS WHEN normal_ascending => -- Count up 0..2T-1 (2T counts) modulo_inc(read_counter); IF read_counter = ac_2t_minus_1 THEN ctr_state <= normal_descending; read_counter <= ac_t_minus_1; read_mem_2t <= TRUE; END IF; -- mem_2T between these two states (1 count) WHEN normal_descending => -- Count down T-1..T (2T counts) modulo_dec(read_counter); IF read_counter = ac_t THEN ctr_state <= short_ascending; read_counter <= ac_1; END IF; WHEN short_ascending => -- Count up 1..T-1 then 2T then T+1..2T-1 (2T-1 counts) modulo_inc(read_counter); IF read_counter = ac_t_minus_1 THEN read_mem_2t <= TRUE; read_counter <= ac_t_plus_1; ELSIF read_counter = ac_2t_minus_1 THEN ctr_state <= offset_descending; read_counter <= ac_0; END IF; WHEN offset_descending => -- Count down 0, T-1..1, T, 2T-1..T+1 (2T counts) modulo_dec(read_counter); IF read_counter = ac_0 THEN
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -