Adding MEMORY to your hardware - the game changer
Everything we've built so far has been memoryless. Change input, output changes. But how do you build a counter? A state machine? A CPU? You need MEMORY.
A D flip-flop captures the value on its D input when the clock ticks, and holds it until the next tick.
// Simple D flip-flop // Captures input on rising clock edge module dff ( input clk, // Clock signal input d, // Data input output reg q // Data output (registered) ); // On rising edge of clock, capture D into Q always @(posedge clk) begin q <= d; end endmodule
always @(posedge clk) = "Do this on rising clock edge"<= = Non-blocking assignment (for sequential logic)reg type = Can hold a value between clock edges
Every real flip-flop needs a reset to put it in a known state at power-up:
// D flip-flop with synchronous reset module dff_reset ( input clk, input rst, // Active-high reset input d, output reg q ); always @(posedge clk) begin if (rst) q <= 1'b0; // Reset to 0 else q <= d; // Normal operation end endmodule
Let's build an 8-bit counter that increments every clock cycle:
// 8-bit counter with reset // Counts from 0 to 255, then wraps to 0 module counter_8bit ( input clk, input rst, output reg [7:0] count ); always @(posedge clk) begin if (rst) count <= 8'd0; else count <= count + 1; // Increment end endmodule
+ operator synthesizes to an 8-bit adder with feedback!
// 8-bit shift register // Shifts data in from 'serial_in', shifts out to 'serial_out' module shift_reg_8bit ( input clk, input rst, input serial_in, output serial_out, output [7:0] parallel_out ); reg [7:0] shift_reg; always @(posedge clk) begin if (rst) shift_reg <= 8'd0; else shift_reg <= {shift_reg[6:0], serial_in}; // Shift left end assign serial_out = shift_reg[7]; // MSB out assign parallel_out = shift_reg; // All bits out endmodule
{shift_reg[6:0], serial_in} takes bits 6-0 and appends serial_in as bit 0. This creates the shift operation!
State machines are the backbone of control logic. Here's a traffic light controller:
// Traffic light state machine // RED → GREEN → YELLOW → RED module traffic_light ( input clk, input rst, output reg red, output reg yellow, output reg green ); // State encoding localparam RED = 2'b00; localparam GREEN = 2'b01; localparam YELLOW = 2'b10; reg [1:0] state, next_state; reg [25:0] timer; // State register always @(posedge clk) begin if (rst) state <= RED; else if (timer == 0) state <= next_state; end // Next state logic always @(*) begin case(state) RED: next_state = GREEN; GREEN: next_state = YELLOW; YELLOW: next_state = RED; default: next_state = RED; endcase end // Output logic always @(*) begin {red, yellow, green} = 3'b000; case(state) RED: red = 1'b1; YELLOW: yellow = 1'b1; GREEN: green = 1'b1; endcase end // Timer (counts clock cycles) always @(posedge clk) begin if (rst || timer == 0) timer <= 27_000_000; // 1 second @ 27MHz else timer <= timer - 1; end endmodule
always @(posedge clk) blocks, ALWAYS use non-blocking assignments (<=). Mixing = and <= causes race conditions and simulation/synthesis mismatches!