Reads in WORD_WIDTH serial bits (MSB first) and signals when the last bit has been read-in and a new word is ready to be read-out, in the same cycle if necessary to receive an uninterrupted serial stream.
When the parallel_out
handshake completes, the serial_in
bits are
shifted-in (MSB first) until all present at parallel_out
, which halts
shifting and asserts parallel_out_valid
. A new handshake can complete in
the same cycle to receive a serial stream without interruption.
Holding clock_enable
low halts any shifting, holding all bits in place
and ignoring any changes at the control and data inputs, except for
clear
.
Asserting clear
will empty the shift register, re-initialize the counter,
drop parallel_out_valid
, and start shifting-in serial bits.
`default_nettype none module Serial_Parallel #( parameter WORD_WIDTH = 0 ) ( input wire clock, input wire clock_enable, input wire clear, output reg parallel_out_valid, input wire parallel_out_ready, output wire [WORD_WIDTH-1:0] parallel_out, input wire serial_in ); `include "clog2_function.vh" localparam WORD_ZERO = {WORD_WIDTH{1'b0}}; localparam COUNT_WIDTH = clog2(WORD_WIDTH); localparam COUNT_ZERO = {COUNT_WIDTH{1'b0}}; localparam COUNT_BITS_INITIAL = WORD_WIDTH - 1; localparam COUNT_BITS_NEXT = WORD_WIDTH - 2; initial begin parallel_out_valid = 1'b0; // We start empty, so ready to shift-in. end
Count the number of bit shifts remaining. When the last bit has been
read-in from serial_in
, the count reaches zero, the counter halts, and we
can immediately read out the word. After the initial word, we reload the
counter with a value one less than initially since the next serial bit is
read in at the same time as the word is read out.
reg counter_run = 1'b0; reg counter_load = 1'b0; wire [COUNT_WIDTH-1:0] count; Counter_Binary #( .WORD_WIDTH (COUNT_WIDTH), .INCREMENT (1), .INITIAL_COUNT (COUNT_BITS_INITIAL [COUNT_WIDTH-1:0]) ) shifts_remaining ( .clock (clock), .clear (clear), .up_down (1'b1), // 0 up, 1 down .run (counter_run), .load (counter_load), .load_count (COUNT_BITS_NEXT [COUNT_WIDTH-1:0]), .carry_in (1'b0), // verilator lint_off PINCONNECTEMPTY .carry_out (), .carries (), .overflow (), // verilator lint_on PINCONNECTEMPTY .count (count) );
The shift register only shifts when the counter is running.
reg shifter_run = 1'b0; Register_Pipeline #( .WORD_WIDTH (1), .PIPE_DEPTH (WORD_WIDTH), .RESET_VALUES (WORD_ZERO) ) shift_register ( .clock (clock), .clock_enable (shifter_run), .clear (clear), .parallel_load (1'b0), .parallel_in (WORD_ZERO), .parallel_out (parallel_out), .pipe_in (serial_in), // verilator lint_off PINCONNECTEMPTY .pipe_out () // verilator lint_on PINCONNECTEMPTY );
Completing the parallel output handshake reloads the counter, starting the
bit shifting. After all the bits have been read-in from serial_in
, the
counter reaches to zero, halts, and raises parallel_out_valid
.
reg handshake_done = 1'b0; always @(*) begin parallel_out_valid = (count == COUNT_ZERO) && (clock_enable == 1'b1); handshake_done = (parallel_out_valid == 1'b1) && (parallel_out_ready == 1'b1); counter_run = (count != COUNT_ZERO) && (clock_enable == 1'b1); counter_load = (handshake_done == 1'b1); shifter_run = (counter_run == 1'b1) || (counter_load == 1'b1); end endmodule