module bin2bcd #( BITS = 8 , BASE = 10 , MAX_SKIP = BITS ) ( input bit clk , input bit reset , output bit bin_ready , input bit bin_valid `define bin_valid $past(bin_valid) , input bit [BITS-1:0] bin_data `define bin_data $past(bin_data) , input bit bcd_ready `define bcd_ready $past(bcd_ready) , output bit bcd_valid , output bit [DIGITS-1:0][BASE_BITS-1:0] bcd_data ); localparam BASE_BITS = $clog2(BASE); localparam DIGITS = $rtoi($ceil($ln(1 << BITS) / $ln(BASE))); bit bin_b_valid; bit [BITS-1:0] bin_b_data; bit [DIGITS-1:0][BASE_BITS-1:0] bcd;; bit [$clog2(BITS):0] work; always_ff @(posedge clk) begin if (reset) begin bin_ready = 0; bcd_valid = 0; bin_b_valid = 0; end else begin if (bin_ready && `bin_valid) begin bin_b_valid = 1; bin_b_data = `bin_data; bcd = 0; work = BITS; for (int i = 0; i < BITS && i < MAX_SKIP; ++i) begin if (bin_b_data[BITS-1]) break; bin_b_data = { bin_b_data[BITS-2:0], 1'b0 }; work = work - 1; end end if (bin_b_valid && work != 0) begin automatic bit carry = bin_b_data[BITS-1]; for (int i = 0; i < DIGITS; ++i) begin {carry, bcd[i]} = {bcd[i], carry}; if ({carry, bcd[i]} >= BASE) begin // verilator lint_off WIDTH bcd[i] = {carry, bcd[i]} - BASE; // verilator lint_on WIDTH carry = 1; end end assert(!carry); bin_b_data = bin_b_data << 1; work = work - 1; end if (`bcd_ready) bcd_valid = 0; if (!bcd_valid && bin_b_valid && work == 0) begin bcd_valid = 1; bcd_data = bcd; bin_b_valid = 0; end bin_ready = !bin_b_valid; end end endmodule