From ee86346db20f3108a1a081807f0058fe931f15f3 Mon Sep 17 00:00:00 2001 From: Jiahui Xu Date: Thu, 6 Nov 2025 15:08:06 +0100 Subject: [PATCH] [HDL] Fix the "run ahead" limitation of selector --- data/verilog/arith/select.v | 148 +++++++++++++++----------------- data/vhdl/arith/select.vhd | 167 ++++++++++++++++++++---------------- 2 files changed, 166 insertions(+), 149 deletions(-) diff --git a/data/verilog/arith/select.v b/data/verilog/arith/select.v index 63c8eb39f5..33bbbc53d4 100644 --- a/data/verilog/arith/select.v +++ b/data/verilog/arith/select.v @@ -1,92 +1,86 @@ `timescale 1ns/1ps -module antitokens ( - // inputs - input clk, - input reset, - input pvalid1, - input pvalid0, - input generate_at1, - input generate_at0, - // outputs - output kill1, - output kill0, - output stop_valid -); - - wire reg_in0; - wire reg_in1; - reg reg_out0 = 1'b0; - reg reg_out1 = 1'b0; - - always @(posedge clk) begin - if (reset) begin - reg_out0 <= 1'b0; - reg_out1 <= 1'b0; - end else begin - reg_out0 <= reg_in0; - reg_out1 <= reg_in1; - end - end - assign reg_in0 = !pvalid0 & (generate_at0 | reg_out0); - assign reg_in1 = !pvalid1 & (generate_at1 | reg_out1); +module selector #( + parameter DATA_TYPE = 8 +)( + // Inputs + input wire clk, + input wire rst, + input wire [0:0] condition, + input wire condition_valid, + input wire [DATA_TYPE-1:0] trueValue, + input wire trueValue_valid, + input wire [DATA_TYPE-1:0] falseValue, + input wire falseValue_valid, + input wire result_ready, + // Outputs + output wire [DATA_TYPE-1:0] result, + output wire result_valid, + output wire condition_ready, + output wire trueValue_ready, + output wire falseValue_ready +); - assign stop_valid = reg_out0 | reg_out1; + // Parameters + localparam discard_depth = 4; + localparam counter_width = $clog2(discard_depth); - assign kill0 = generate_at0 | reg_out0; - assign kill1 = generate_at1 | reg_out1; + // Internal signals + reg [counter_width-1:0] num_token_to_discard_true = 0; + reg [counter_width-1:0] num_token_to_discard_false = 0; -endmodule + wire can_propagate_true; + wire can_propagate_false; + wire can_discard_true; + wire can_discard_false; + wire still_need_to_discard_true; + wire still_need_to_discard_false; + // ---------------------- + // Internal signal logic + // ---------------------- + assign can_discard_true = (trueValue_valid || (num_token_to_discard_true < discard_depth)) ? 1'b1 : 1'b0; + assign can_discard_false = (falseValue_valid || (num_token_to_discard_false < discard_depth)) ? 1'b1 : 1'b0; -module selector #( - parameter DATA_TYPE = 32 -)( - // inputs - input clk, - input rst, - input condition, - input condition_valid, - input [DATA_TYPE-1 : 0] trueValue, - input trueValue_valid, - input [DATA_TYPE-1 : 0] falseValue, - input falseValue_valid, - input result_ready, - // outputs - output [DATA_TYPE-1 : 0] result, - output result_valid, - output condition_ready, - output trueValue_ready, - output falseValue_ready -); + assign can_propagate_true = (condition_valid && condition[0] && trueValue_valid && + (num_token_to_discard_true == 0) && can_discard_false) ? 1'b1 : 1'b0; - wire ee, validInternal, kill0, kill1, antitokenStop, g0, g1; + assign can_propagate_false = (condition_valid && !condition[0] && falseValue_valid && + (num_token_to_discard_false == 0) && can_discard_true) ? 1'b1 : 1'b0; - // condition and one input - assign ee = condition_valid & ( ( !condition & falseValue_valid) | (condition & trueValue_valid) ); - // propagate ee if not stopped antitoken - assign validInternal = ee & !antitokenStop; + assign still_need_to_discard_true = (num_token_to_discard_true > 0) ? 1'b1 : 1'b0; + assign still_need_to_discard_false = (num_token_to_discard_false > 0) ? 1'b1 : 1'b0; - assign g0 = !trueValue_valid & validInternal & result_ready; - assign g1 = !falseValue_valid & validInternal & result_ready; + // ---------------------- + // Handshake signals + // ---------------------- + assign result_valid = can_propagate_true || can_propagate_false; + assign result = condition[0] ? trueValue : falseValue; - assign result_valid = validInternal; - assign trueValue_ready = !trueValue_valid | (validInternal & result_ready) | kill0; // normal join or antitoken - assign falseValue_ready = !falseValue_valid | (validInternal & result_ready) | kill1; // normal join or antitoken - assign condition_ready = !condition_valid | (validInternal & result_ready); // normal join + assign trueValue_ready = (~trueValue_valid) || (result_valid && result_ready) || still_need_to_discard_true; + assign falseValue_ready = (~falseValue_valid) || (result_valid && result_ready) || still_need_to_discard_false; + assign condition_ready = (~condition_valid) || (result_valid && result_ready); - assign result = condition ? trueValue : falseValue; + // ---------------------- + // Discard counters + // ---------------------- + always @(posedge clk) begin + if (rst) begin + num_token_to_discard_true <= 0; + num_token_to_discard_false <= 0; + end else begin + // True counter update + if (result_valid && result_ready && !trueValue_valid) + num_token_to_discard_true <= num_token_to_discard_true + 1; + else if (still_need_to_discard_true && trueValue_valid) + num_token_to_discard_true <= num_token_to_discard_true - 1; - antitokens antitokens ( - .clk(clk), - .reset(rst), - .pvalid0(trueValue_valid), - .pvalid1(falseValue_valid), - .generate_at0(g0), - .generate_at1(g1), - .kill0(kill0), - .kill1(kill1), - .stop_valid(antitokenStop) - ); + // False counter update + if (result_valid && result_ready && !falseValue_valid) + num_token_to_discard_false <= num_token_to_discard_false + 1; + else if (still_need_to_discard_false && falseValue_valid) + num_token_to_discard_false <= num_token_to_discard_false - 1; + end + end endmodule diff --git a/data/vhdl/arith/select.vhd b/data/vhdl/arith/select.vhd index b15057c840..37aa244cb2 100644 --- a/data/vhdl/arith/select.vhd +++ b/data/vhdl/arith/select.vhd @@ -1,55 +1,7 @@ library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; - -entity antitokens is - port ( - clk, rst : in std_logic; - pvalid1, pvalid0 : in std_logic; - kill1, kill0 : out std_logic; - generate_at1, generate_at0 : in std_logic; - stop_valid : out std_logic - ); -end antitokens; - -architecture arch of antitokens is - signal reg_in0, reg_in1, reg_out0, reg_out1 : std_logic; -begin - - reg0 : process (clk) - begin - if (rising_edge(clk)) then - if (rst = '1') then - reg_out0 <= '0'; - else - reg_out0 <= reg_in0; - end if; - end if; - end process reg0; - - reg1 : process (clk) - begin - if (rising_edge(clk)) then - if (rst = '1') then - reg_out1 <= '0'; - else - reg_out1 <= reg_in1; - end if; - end if; - end process reg1; - - reg_in0 <= not pvalid0 and (generate_at0 or reg_out0); - reg_in1 <= not pvalid1 and (generate_at1 or reg_out1); - - stop_valid <= reg_out0 or reg_out1; - - kill0 <= generate_at0 or reg_out0; - kill1 <= generate_at1 or reg_out1; -end architecture; - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; +use ieee.math_real.all; entity selector is generic ( @@ -76,33 +28,104 @@ entity selector is end entity; architecture arch of selector is - signal ee, validInternal : std_logic; - signal kill0, kill1 : std_logic; - signal antitokenStop : std_logic; - signal g0, g1 : std_logic; -begin + -- One side can run ahead of the other for "discard_depth - 1" times + -- NOTE: discard_depth > 1 + constant discard_depth : integer := 4; + constant counter_width : positive := positive(ceil(log2(real(discard_depth)))); - ee <= condition_valid and ((not condition(0) and falseValue_valid) or (condition(0) and trueValue_valid)); --condition(0) and one input - validInternal <= ee and not antitokenStop; -- propagate ee if not stopped by antitoken + -- number of tokens to discard for the true side + signal num_token_to_discard_true : std_logic_vector(counter_width - 1 downto 0) := (others => '0'); + -- number of tokens to discard for the false side + signal num_token_to_discard_false : std_logic_vector(counter_width - 1 downto 0) := (others => '0'); - g0 <= not trueValue_valid and validInternal and result_ready; - g1 <= not falseValue_valid and validInternal and result_ready; + signal can_propagate_true : std_logic; + signal can_propagate_false : std_logic; - result_valid <= validInternal; - trueValue_ready <= (not trueValue_valid) or (validInternal and result_ready) or kill0; -- normal join or antitoken - falseValue_ready <= (not falseValue_valid) or (validInternal and result_ready) or kill1; --normal join or antitoken - condition_ready <= (not condition_valid) or (validInternal and result_ready); --like normal join + signal can_discard_true : std_logic; + signal can_discard_false : std_logic; + + signal still_need_to_discard_true : std_logic; + signal still_need_to_discard_false : std_logic; +begin - result <= falseValue when (condition(0) = '0') else - trueValue; - Antitokens : entity work.antitokens - port map( - clk, rst, - falseValue_valid, trueValue_valid, - kill1, kill0, - g1, g0, - antitokenStop - ); + -- [START internal signal configuration] + -- Select can discard more true if we are currently discarding a true or the counter hasn't reached the limit + can_discard_true <= '1' when ((trueValue_valid = '1') or (unsigned(num_token_to_discard_true) < discard_depth)) else '0'; + -- Select can discard more false if we are currently discarding a true or the counter hasn't reached the limit + can_discard_false <= '1' when ((falseValue_valid = '1') or (unsigned(num_token_to_discard_false) < discard_depth)) else '0'; + + can_propagate_true <= '1' when + ((condition_valid = '1') and + (condition(0) = '1') and + (trueValue_valid = '1') and + (unsigned(num_token_to_discard_true) = 0) and + (can_discard_false = '1')) + else '0'; + + can_propagate_false <= '1' when + ((condition_valid = '1') and + (condition(0) = '0') and + (falseValue_valid = '1') and + (unsigned(num_token_to_discard_false) = 0) and + (can_discard_true = '1')) + else '0'; + + still_need_to_discard_true <= '1' when (unsigned(num_token_to_discard_true) > 0) else '0'; + still_need_to_discard_false <= '1' when (unsigned(num_token_to_discard_false) > 0) else '0'; + -- [END internal signal configuration] + + -- [START handshake configuration] + -- true (false) token is valid to send if + -- 1. there is nothing to discard for true (false) + -- 2. the data is valid + -- 3. the counter for false (true) is less than maximum + result_valid <= can_propagate_true or can_propagate_false; + result <= falseValue when (condition(0) = '0') else trueValue; + + -- Conditions: + -- 1. "not trueValue_valid": prevent deadlocking. + -- 2. "result_valid and result_ready": all three inputs are valid. In this case, take all of them and discard the unselected one. + -- 3. "discard_true": false input and condition are passed without the true input in a previous cycle. Here we need to discard that one. + trueValue_ready <= (not trueValue_valid) or (result_valid and result_ready) or (still_need_to_discard_true); + falseValue_ready <= (not falseValue_valid) or (result_valid and result_ready) or (still_need_to_discard_false); + condition_ready <= (not condition_valid) or (result_valid and result_ready); + -- [END handshake configuration] + + -- [START updating the discard counters] + proc_counter_true : process (clk) + begin + if (rising_edge(clk)) then + if (rst = '1') then + num_token_to_discard_true <= (others => '0'); + else + -- false token transfered without discarding true token + if (result_valid and result_ready and (not trueValue_valid)) then + num_token_to_discard_true <= std_logic_vector(unsigned(num_token_to_discard_true) + 1); + -- discarding the true token while not taking any new false token + elsif (still_need_to_discard_true and trueValue_valid) then + num_token_to_discard_true <= std_logic_vector(unsigned(num_token_to_discard_true) - 1); + end if; + end if; + end if; + end process proc_counter_true; + + proc_counter_false : process (clk) + begin + if (rising_edge(clk)) then + if (rst = '1') then + num_token_to_discard_false <= (others => '0'); + else + -- false token transfered without discarding true token + if (result_valid and result_ready and (not falseValue_valid)) then + num_token_to_discard_false <= std_logic_vector(unsigned(num_token_to_discard_false) + 1); + -- discarding the false token while not taking any new false token + elsif (still_need_to_discard_false and falseValue_valid) then + num_token_to_discard_false <= std_logic_vector(unsigned(num_token_to_discard_false) - 1); + end if; + end if; + end if; + end process proc_counter_false; + -- [END updating the discard counters] end architecture;