Verilog Implementation of 74HC595 Shift Register Driver
74HC595 Overview
The 74HC595 is an 8-bit serial-in, parallel-out shift register featuring storage capabilities and tri-state outputs. This integrated circuit efficiently converts serial data into parallel output, making it ideal for applications requiring expanded output capacity while conserving FPGA I/O resources.
Pin Functionality
- SHCP: Shift register clock input - Data is shifted into the register on the rising edge of this signal
- STCP: Storage register clock input - After transferring the complete data sequence, a high-level clock signal activates to latch the data
- /OE: Output enable input - Typically active low (implementation-specific)
- DS: Serial data input - The data stream enters through this pin
These four signals are generated by our Verilog HDL module to properly drive the 74HC595 chip.
Timing Architecture
This implementation utilizes two cascaded 74HC595 devices to handle 12-bit serial data. The shift clock (SH_CP) is derived from a divided system clock signal. The storage clock (STCP) is asserted after all 12 bits have been transferred, effectively latching the data into the output registers. The output enable (/OE) is held at a low level, though in this specific design, it's configured as high due to board-level constraints.
Driver Implementation
module ShiftRegister_Controller(
input sys_clk,
input reset_n,
input [7:0] segment_data,
input [3:0] digit_select,
output reg shift_clk,
output reg storage_clk,
output reg data_serial,
output output_enable
);
// Internal signal declarations
reg [2:0] clock_divider = 3'd0;
reg [3:0] bit_counter = 4'd0;
wire [11:0] parallel_data;
// Clock divider process
always @(posedge sys_clk) begin
if(!reset_n)
clock_divider <= 3'd0;
else if(clock_divider == 3)
clock_divider <= 3'd0;
else
clock_divider <= clock_divider + 1'b1;
end
// Bit counter process
always @(posedge sys_clk) begin
if(!reset_n)
bit_counter <= 4'd0;
else if((bit_counter == 11) && (clock_divider == 3))
bit_counter <= 4'd0;
else if(clock_divider == 3)
bit_counter <= bit_counter + 1'b1;
else
bit_counter <= bit_counter;
end
// Shift clock generation
always @(posedge sys_clk) begin
if(!reset_n)
shift_clk <= 1'b0;
else if(clock_divider >= 4'd2)
shift_clk <= 1'b1;
else
shift_clk <= 1'b0;
end
// Storage clock generation
always @(posedge sys_clk) begin
if(!reset_n)
storage_clk <= 1'b0;
else if((bit_counter == 4'd11) && (clock_divider == 3))
storage_clk <= 1'b1;
else
storage_clk <= 1'b0;
end
// Parallel data concatenation
assign parallel_data = {segment_data[0],segment_data[1],segment_data[2],segment_data[3],
segment_data[4],segment_data[5],segment_data[6],segment_data[7],
digit_select};
// Serial data output
always @ (posedge sys_clk) begin
if(reset_n == 1'b0)
data_serial <= 1'b0;
else if(clock_divider == 3'd0)
data_serial <= parallel_data[bit_counter];
else
data_serial <= data_serial;
end
assign output_enable = 1'b1;
endmodule
Testbench
`timescale 1ns/1ps
module ShiftRegister_Controller_TB;
reg system_clock;
reg reset_n;
reg [7:0] segment_data;
reg [3:0] digit_select;
wire shift_clk;
wire storage_clk;
wire data_serial;
wire output_enable;
ShiftRegister_Controller Controller_DUT(
.sys_clk ( system_clock ),
.reset_n ( reset_n ),
.segment_data ( segment_data ),
.digit_select ( digit_select ),
.shift_clk ( shift_clk ),
.storage_clk ( storage_clk ),
.data_serial ( data_serial ),
.output_enable ( output_enable )
);
// Clock generation
initial
system_clock = 1'b0;
always #10 system_clock = ~system_clock;
// Test sequence
initial
begin
#1;
reset_n = 1'b0;
segment_data = 8'd0;
digit_select = 4'd0;
#21;
reset_n = 1'b1;
#20;
segment_data = 8'b1010_1010;
digit_select = 4'b1111;
end
endmodule
Simulation
The design has been verified using ModelSim simulation, confirming proper timing relationships and data transfer functionality.