diff --git a/04_arithmetics_and_pipelining/04_10_11_distributor/03_08_float_discriminant.sv b/04_arithmetics_and_pipelining/04_10_11_distributor/03_08_float_discriminant.sv new file mode 100644 index 00000000..c441392a --- /dev/null +++ b/04_arithmetics_and_pipelining/04_10_11_distributor/03_08_float_discriminant.sv @@ -0,0 +1,184 @@ +//---------------------------------------------------------------------------- +// Task +//---------------------------------------------------------------------------- + +module float_discriminant ( + input clk, + input rst, + + input arg_vld, + input [FLEN - 1:0] a, + input [FLEN - 1:0] b, + input [FLEN - 1:0] c, + + output logic res_vld, + output logic [FLEN - 1:0] res, + output logic res_negative, + output logic err, + + output logic busy +); + + // Task: + // Implement a module that accepts three Floating-Point numbers and outputs their discriminant. + // The resulting value res should be calculated as a discriminant of the quadratic polynomial. + // That is, res = b^2 - 4ac == b*b - 4*a*c + // + // Note: + // If any argument is not a valid number, that is NaN or Inf, the "err" flag should be set. + // + // The FLEN parameter is defined in the "import/preprocessed/cvw/config-shared.vh" file + // and usually equal to the bit width of the double-precision floating-point number, FP64, 64 bits. + + + + //------------------------------------------------------------------------ + // вариант 2 FSM + //------------------------------------------------------------------------ + + // States + enum logic [2:0] + { + st_idle = 3'd0, + st_1 = 3'd1, + st_2 = 3'd2, + st_3 = 3'd3, + st_4 = 3'd4, + st_5 = 3'd5 + + + } + state, next_state; + + logic [FLEN - 1:0] f_mul_a, f_mul_b, f_mul_res; // connectors for multiplier + logic [FLEN - 1:0] b_b, ac; // tmp result; + logic [FLEN - 1:0] b_tmp; // b - lach + logic f_mul_busy, f_mul_error, f_mul_arg_vld; + //--------------------------------------------------------------------- + f_mult f_mul_i( // module multiplier + .clk(clk), + .rst(rst), + .a(f_mul_a), + .b(f_mul_b), + .up_valid(f_mul_arg_vld), + .res(f_mul_res), + .down_valid(f_mul_res_vld), + .busy(f_mul_busy), + .error(f_mul_error) + ); + + //------------------------------------------------------------------------ + + logic f_sub_arg_vld, f_sub_res_vld, f_sub_error; + + f_sub f_sub_i( + .clk(clk), + .rst(rst), + .a(b_b), // аргументы берем из предыдущих + .b(ac), // результатаов + .up_valid(f_sub_arg_vld), + .res(res), // подключаем к выходу модуля + .down_valid(f_sub_res_vld), + .busy(sub_busy), + .error(f_sub_error) + ); + + //------------------------------------------------------------------------ + + always_comb + begin + next_state = state; + err = '0; // ошибки не обнаружены + + case (state) + st_idle: + begin + f_mul_a = a; + f_mul_b = c; + res_vld = '0; + if (arg_vld) begin + f_mul_arg_vld = '1 ; // если входные данные валидны запускаем вычисление a*c + busy = '1 ; // выставдяем флаг занятости + next_state = st_1 ; // и переходим к следующей стадии возведение в квадрат + b_tmp = b; + end + + end + st_1: + begin // умножитель у нас конвейерный на следующий такт запускаем возведение в степень b^2 + f_mul_a = b_tmp ; // загружаем умножитель значениями b + f_mul_b = b_tmp ; // и b + f_mul_arg_vld = '1 ; // запускаем вычисление + next_state = st_2 ; // на следующую стадию + end + + st_2: + begin + f_mul_arg_vld = '0; + if (f_mul_error) begin + err = '1; // и выставляем флаг ошибки + end + if (f_mul_res_vld) begin // ждем окончания умножения a*c + f_mul_a = f_mul_res ; // загружаем умножитель значениями а*c + f_mul_b = $realtobits(4) ; // и 4 + f_mul_arg_vld = '1 ; // запускаем умножение 4*(a*c) + next_state = st_3; // и на следующую стадию + end + end + + st_3: + begin + f_mul_arg_vld = '0 ; + if (f_mul_error) begin + err = '1 ; // за одно выставляем флаг ошибки + end + // так как мы на втором такте загрузили b*b + // проверку на готовность можно пропустить + b_b = f_mul_res ; // сохраняем результат + next_state = st_4 ; // к следующей стадии + end + + st_4: + begin + f_mul_arg_vld = '0 ; + if (f_mul_error) begin + err = '1 ; // и выставляем флаг ошибки + end + if (f_mul_res_vld) begin // умножение 4*ac закончилось + ac = f_mul_res ; // сохраняем результат + // аргументы к вычитателю подключены + f_sub_arg_vld = '1 ; // запускаем вычитание + next_state = st_5 ; // к следующей стадии + end + end + + st_5: + begin + if (f_sub_error) begin + err = '1 ; // и выставляем флаг ошибки + + end + if (f_sub_res_vld) begin // вычитание b^2 - 4ac закончилось + res_vld = '1 ; // выставляем флаг готовности + busy = '0 ; // сбрасываем флаг занятости + f_sub_arg_vld = '0 ; // данные на входе вычитателя не актуальны + next_state = st_idle ; + end + end + endcase + end + + //------------------------------------------------------------------------ + // Assigning next state + + always_ff @ (posedge clk) + if (rst) + state <= st_idle; + else + state <= next_state; + + //------------------------------------------------------------------------ + + // конец 2 варианта FSM + +endmodule diff --git a/04_arithmetics_and_pipelining/04_10_11_distributor/04_10_11_distributor.sv b/04_arithmetics_and_pipelining/04_10_11_distributor/04_10_11_distributor.sv new file mode 100644 index 00000000..6b4ba036 --- /dev/null +++ b/04_arithmetics_and_pipelining/04_10_11_distributor/04_10_11_distributor.sv @@ -0,0 +1,92 @@ +localparam LATENCY = 10 ; // латентность конвейера перенести в svh файл + +// модуль имитируюший работу конвейера путем импементации нескольких N=LATENCY +// вычислителей с латентностью = LATENCY + +// вижу несколько вариантов оформления решения +// первый выбор входов вычислителя идет через функцию "И" сигнала arg_vld и n-ного разряда one_hot +// регистра-селектора. При активном arg_vld, в каждом такте регистр-селектор сдвигается на 1 разряд +// выходы вычислителей через мультиплексор подключены к выходу модуля. При наличии сигнала res_vld +// каждый такт инкрементирует регистр-селектор выходов n_out. Сигнал res_vld формируется из входного +// сигнала arg_vld c задержкой в LATENCY тактов, в этом задержка реализуется на сдвиговом регистре +// delay_vld. Модули вычислителей могут быть подключены прямым текстом (copy - paste LATENCY раз), +// а могут через generate. + +module distributor + ( + input clk, + input rst, + + input arg_vld, + input [FLEN - 1:0] a, + input [FLEN - 1:0] b, + input [FLEN - 1:0] c, + + output logic res_vld, + output logic [FLEN - 1:0] res, + output logic res_negative, + output logic err, + + output logic busy +); + + logic [LATENCY-1:0] ptr_in; // one-hot registre + logic [$clog2(LATENCY-1):0] n_out; // регистр хранения номера вычислителя с валидным результатом + + logic [LATENCY-1:0] res_vld_o, res_negative_o, err_o, busy_o; // векторы для подключения выходных сигналов вычислителей + logic [FLEN - 1:0] res_o [LATENCY-1:0]; + + always_ff @ (posedge clk) begin + if (rst) begin + ptr_in <= 1; // загружаем "1" в one-hot регистр селектор + n_out <='0; // "0" в регистр-селектор выходов вычислителей + + end + else begin + if (arg_vld) + ptr_in <= {ptr_in[LATENCY-2:0],ptr_in[LATENCY-1]}; // пришли данные - сдвигаем входной регистр-селектор маски + + + if (|res_vld_o) begin // если на выходе готовы данные то + if (n_out == (LATENCY-1 )) n_out <= '0; // и если дошли до последнего вычислителя то сбрасываем номер вычислителя + else n_out <= n_out + 1 ; // иначе увеличиваем номер вычислителя подключенного к выходу + end + + end + end + + genvar i; + + +// создаем модули вычислителей в количестве LATENCY + generate + for(i = 0; i < LATENCY; i = i + 1) begin:calc + + float_discriminant inst ( + .clk(clk), + .rst(rst), + .arg_vld(arg_vld & ptr_in[i]), + .a(a), + .b(b), + .c(c), + + .res_vld(res_vld_o[i]), + .res(res_o[i]), + .res_negative(res_negative_o[i]), + .err(err_o[i]), + .busy(busy_o[i]) + ); + + end + endgenerate + + + always_comb begin + res = res_o[n_out] ; + res_negative = res_negative_o[n_out]; + err = err_o[n_out]; + res_vld = res_vld_o[n_out]; + end + + +endmodule \ No newline at end of file diff --git a/04_arithmetics_and_pipelining/04_10_11_distributor/destributor.pdf b/04_arithmetics_and_pipelining/04_10_11_distributor/destributor.pdf new file mode 100644 index 00000000..8c5d7fa6 Binary files /dev/null and b/04_arithmetics_and_pipelining/04_10_11_distributor/destributor.pdf differ diff --git a/04_arithmetics_and_pipelining/04_10_11_distributor/testbench.sv b/04_arithmetics_and_pipelining/04_10_11_distributor/testbench.sv new file mode 100644 index 00000000..3181e519 --- /dev/null +++ b/04_arithmetics_and_pipelining/04_10_11_distributor/testbench.sv @@ -0,0 +1,339 @@ +//---------------------------------------------------------------------------- +// Testbench +//---------------------------------------------------------------------------- + +// за основу взят testbench задания 03_08_float_discriminant + +`include "util.svh" + +module testbench; + //-------------------------------------------------------------------------- + // Signals to drive Device Under Test - DUT + + logic clk; + logic rst; + + logic arg_vld; + logic [FLEN - 1:0] a; + logic [FLEN - 1:0] b; + logic [FLEN - 1:0] c; + + wire res_vld; + wire [FLEN - 1:0] res; + wire res_negative; + wire err; + + wire busy; + + //-------------------------------------------------------------------------- + // Instantiating DUT + + // float_discriminant dut (.*); + + distributor dut(.*); + + //-------------------------------------------------------------------------- + // Driving clk + + initial + begin + clk = '1; + + forever + begin + # 5 clk = ~ clk; + end + end + + //------------------------------------------------------------------------ + // Reset + + task reset (); + + rst <= 'x; + repeat (3) @ (posedge clk); + rst <= '1; + repeat (3) @ (posedge clk); + rst <= '0; + + endtask + + //-------------------------------------------------------------------------- + // Test ID for error messages + + string test_id; + + initial $sformat (test_id, "%s", `__FILE__); + + //-------------------------------------------------------------------------- + // Driving stimulus + + localparam TIMEOUT = 5000; + + task run (); + + `ifdef USE_FORK_JOIN_NONE + + // Setting timeout against hangs + + fork + begin + repeat (TIMEOUT) @ (posedge clk); + $display ("FAIL %s: timeout!", test_id); + $finish; + end + join_none + + `endif + + $display ("--------------------------------------------------"); + $display ("Running %m"); + + // Init and reset + + arg_vld <= '0; + reset (); + + // Direct testing - a single test + + a <= $realtobits ( 1 ); + b <= $realtobits ( 4 ); + c <= $realtobits ( 3 ); + arg_vld <= '1; + + @ (posedge clk); + arg_vld <= '0; + +// while (~ res_vld) +// @ (posedge clk); + + // Direct testing - a group of tests (5 times) + + for (int i = 0; i < 100; i = i * 3 + 1) + begin + a <= $realtobits ( i ); // отправляем данные в вычислитель + b <= $realtobits ( i+10 ); + c <= $realtobits ( i ); + arg_vld <= '1; // выставляем флаг готовности данных + + @ (posedge clk); + arg_vld <= '0; // ждем 1 такт и сбрасываем флаг готовности данных + + end + + + repeat ( 5 ) @ (posedge clk); // ждем 5 тактов проверка режима старт-стоп + + + // Random testing + repeat (2*LATENCY) // количество циклов превышает латентность для проверки перехода от последнего вычислителя к нулевому + // + begin + a <= $realtobits ( $urandom () / 1000.0 ) ; // данные передаем в вычислитель + b <= $realtobits ( $urandom () / 1000.0 ) ; + c <= $realtobits ( $urandom () / 1000.0 ) ; + arg_vld <= '1; // готовность входных данных + + @ (posedge clk); + arg_vld <= '0; + end + + + repeat (LATENCY+1 ) @ (posedge clk); // ждем особождения конвейера + + + + `ifdef USE_FORK_JOIN_NONE + + // Disabling timeout check + disable fork; + + `endif + + endtask + + //-------------------------------------------------------------------------- + // Running testbench + + initial + begin + `ifdef __ICARUS__ + // Uncomment the following line + // to generate a VCD file and analyze it using GTKwave + + $dumpvars; + `endif + + run (); + + $finish; + end + + //-------------------------------------------------------------------------- + // Utility tasks and functions + + function is_err ( [FLEN - 1:0] a_bits ); + return a_bits [FLEN - 2 -: NE] === '1; + endfunction + + //-------------------------------------------------------------------------- + // Logging + + int unsigned cycle = 0; + + always @ (posedge clk) + begin + $write ("%s time %7d cycle %5d", test_id, $time, cycle); + cycle <= cycle + 1'b1; + + if (rst) + $write (" rst"); + else + $write (" "); + + if (arg_vld) + // Optionnaly change to `PF_BITS optionally + $write (" arg %s %s %s", `PG_BITS (a), `PG_BITS (b), `PG_BITS (c) ); + else + $write (" "); + + if (res_vld) + $write (" res %s", `PG_BITS(res) ); + + $display; + end + + //-------------------------------------------------------------------------- + // Modeling and checking + + logic [FLEN - 1:0] queue [$]; + logic [FLEN - 1:0] res_expected; + logic err_expected; + + logic was_reset = 0; + + // Blocking assignments are okay in this synchronous always block, because + // data is passed using queue and all the checks are inside that always + // block, so no race condition is possible + + // verilator lint_off BLKSEQ + + always @ (posedge clk) + begin + if (rst) + begin + queue = {}; + was_reset = 1; + end + else if (was_reset) + begin + if (arg_vld) + begin + res_expected = $realtobits( $bitstoreal (b) * $bitstoreal (b) - 4 * $bitstoreal (a) * $bitstoreal (c) ); + queue.push_back (res_expected); + + end + + + if (res_vld) + begin + if (queue.size () == 0) + begin + $display ("FAIL %s: unexpected result %s", + test_id, `PG_BITS (res) ); + + $finish; + end + else + begin + `ifdef __ICARUS__ + // Some version of Icarus has a bug, and this is a workaround + res_expected = queue [0]; + queue.delete (0); + `else + res_expected = queue.pop_front () + `endif + + err_expected = is_err ( res_expected ); + + if (err !== err_expected ) + begin + $display ("FAIL %s: error mismatch. Expected %s, actual %s", + test_id, `PB (err_expected), `PB (err) ); + + $finish; + end + else if ( ( err_expected === 1'b0 ) && ( res !== res_expected ) ) + begin + $display ("FAIL %s: res mismatch. Expected %s, actual %s", + test_id, `PG_BITS (res_expected), `PG_BITS (res) ); + + $finish; + end + end + end + end + end + + // verilator lint_on BLKSEQ + + //---------------------------------------------------------------------- + + final + begin + if (queue.size () == 0) + begin + $display ("PASS %s", test_id); + end + else + begin + $write ("FAIL %s: data is left sitting in the model queue:", + test_id); + + for (int i = 0; i < queue.size (); i ++) + $write (" %h", queue [queue.size () - i - 1]); + + $display; + end + end + + //---------------------------------------------------------------------- + // Performance counters + + logic [32:0] n_cycles, arg_cnt, res_cnt; + + always @ (posedge clk) + if (rst) + begin + n_cycles <= '0; + arg_cnt <= '0; + res_cnt <= '0; + end + else + begin + n_cycles <= n_cycles + 1'd1; + + if (arg_vld) + arg_cnt <= arg_cnt + 1'd1; + + if (res_vld) + res_cnt <= res_cnt + 1'd1; + end + + //---------------------------------------------------------------------- + + final + $display ("\n\n number of transfers : arg %0d res %0d per %0d cycles", + arg_cnt, res_cnt, n_cycles); + + //---------------------------------------------------------------------- + // Setting timeout against hangs + + initial + begin + repeat (TIMEOUT) @ (posedge clk); + $display ("FAIL %s: timeout!", test_id); + $finish; + end + +endmodule