Day 1 of making a UART š“‡² I made a SIPO shift register

I donā€™t think Iā€™ve mentioned this on here before but, Iā€™m loosely following the fromthetransistor course here and there when I have the time and energy.
Obviously, Iā€™ve made this work correctly so I know how to do it. But I wanted to write this down to see how well I could articulate the concepts Iā€™ve learned.

The first place I went to was wikipedia and I found this exact diagram.

SIPO shift register

It made the concept of a shift register much clearer and gave me the blueprint on what I should be working on to make it happen. The first thing this inspired me to make was a D flip flop which I learned about in this article and by doing some exercises on HDLBits. This ressource was also very helpful to my understanding of flip flops and latches.

D Flip-flop

This is the first thing I had to implement because itā€™s the main piece of the puzzle. The flip flop is what holds the data in transit, itā€™s a very basic form of memory. Hereā€™s my dff module in verilog:

module dff (
    input d,
    input clk,
    input reg reset,
    output reg q
);

always @ (posedge clk or negedge clk) begin
    if (reset) begin
        q <= 1'b0;
    end else begin
        q <= d;
    end
end
    
endmodule

You can see how this closely reflects the flip flops in the previous image, the only thing thatā€™s missing here is the set input, but itā€™s not something that I felt was necessary for our use case so I simply decided not to include it. As you can see itā€™s pretty simple, the output q updates to match the input d at posedge or negedge of clk, unless reset is active high, in which case q is set to 0.

SIPO

As the name states (Serial-in parallel-out) the goal of this register is to take serial data and convert it to parallel. Meaning, it will come in bit-by-bit and come out as a data block.

To hold values, I implemented a simple wire called hold_value and used ternary operators on the flip flop instantiationsā€™ input d pins, that serves as a multiplexer that choses between shifting in new data or recycling its own output.

Hereā€™s my SIPO module in verilog:

module sipoUnit #(parameter WIDTH = 8) (
    input wire data_in,
    input wire hold_value,
    input wire reset,
    input wire clk,
    output reg [WIDTH-1:0] q
);

    dff dff0 (
        .d(hold_value ? q[0] : data_in),
        .reset(reset),
        .clk(clk),
        .q(q[0])
        );

    generate
        for (genvar i = 0; i < WIDTH-1; i++) begin
            dff dff_inst (
                .d(hold_value ? q[i+1] : q[i]),
                .reset(reset),
                .clk(clk),
                .q(q[i+1])
            );
        end
    endgenerate

endmodule

I feel that there is nothing else to add. Really, the SIPO register seemed daunting to me at first but when I started breaking the pieces down it became clearer what the next implementation should be.
If thereā€™s anything thatā€™s unclear, or you need clarification on any of the decisions made in this code, feel free to contact me on twitter: @pindjouf.