- We can design combinational circuits using
- Gate-level structural design
- Assign statements
- Behavioural always blocks
- Important to consider that our circuits are purely combinational in all cases
- It is possible to design sequential circuits
- Most designs will be synchronous: synced with a clock
- Two inputs
- Two outputs
- Two NOR gates
module srlatch(input R, S output Q, Qbar); nor N1 (Q, R, Qbar); nor N2 (Qbar, S, Q); // Alternatively assign Q = R ~| Qbar; assign Qbar = S ~| Q; endmodule
A D latch is synchronous, where an SR isn't:
module dlatch(input EN, D, output reg Q, Qbar); always @ (D, EN) if(EN) begin Q <= D; Qbar <= ~D; end end endmodule;
D goes to Q if enable is high: circuit is described succinctly.
Generally, FPGA designs will be synchronous as it allows us to more easily understand the timing of the circuit. Most of the logic we will look at will be edge-triggered, which is described as follows:
module simplereg(input d, clk, output reg q); always @ (posedge clk) q <= d; endmodule
posedge keyword can can be used in a sensitivity list to define a trigger on the rising edge of a clock (
negedge is also a thing). In this case, a simple register is created. A multi-bit register/flip flop is defined below:
module simplereg(input [3:0] d, input clk, output reg [3:0] q); always @ (posedge clk) q <= d; endmodule
- All circuits should be synchronised based on the same clock signal
- Clock can be named whatever (usually
clk) and defined as an input to the module
- We often need to reset the contents of a register or state of circuit to 0/a default
- Two types of reset:
- Asynchronous: whenever the reset input is asserted, the reset is triggered
- Synchronous: if the reset is asserted on the rising edge, reset is triggered
- In modern FPGA design, we use synchronous reset
- Two types of reset:
An 8 bit register with synchronous reset:
module 8bitreg(input [7:0] d, input clk, rst, output reg [7:0] q); always @ (posedge clk) begin if(rst) q <= 8'b00000000; else q <= d; end endmodule
For an asynchronous reset, the reset signal is added to the sensitivity list so that the block can be triggered independently of the clock. However, this will desynchronise the always block from the rest of the circuit so is not the prefferred way to do it.
module 8bitreg(input [7:0] d, input clk, rst, output reg [7:0] q); always @ (posedge clk or posedge rst) begin if(rst) q <= 8'b00000000; else q <= d; end endmodule
Can control multiple registers from the same block. Each assignment in a synchronous always block creates a register controlled by the same block. This verilog module contains 3 8-bit registers.
module multireg(input [7:0] a, b, c input clk, rst output reg [7:0] q, r, s); always @ (posedge clk) begin if(!rst) begin q <= 0; r <= 0; s <= 0; end else begin q <= a; r <= b; s <= c; end end endmodule
- When drawing can ignore clock and reset as they should always be there
- Putting a triangle on an input in a block diagram shows that the input is edge-triggered
<=operator is called non-blocking assignment
- For combinational always blocks, as use blocking assignment and order matters
- For a synchronous block, order does not matter
- Everything only happens on the rising edge
A register where the value increments on the rising edge (or decrements if
down signal is asserted).
module simplecount(input clk, rst, down, output reg [3:0] q); always @ (posedge clk) begin if(rst) q <= 4'b0000; else if(down) q <= q - 1'b1; else q <= q + 1'b1; endmodule
Can alter to include an
enable signal. Since it's a synchronous component, don't need to account for all branches.
module simplecount(input clk, rst, down, enable, output reg [3:0] q); always @ (posedge clk) begin if(rst) q <= 4'b0000; else if(enable) if(down) q <= q - 1'b1; else q <= q + 1'b1; end endmodule
Can again alter to include the ability to load a value.
module simplecount(input clk, rst, down, load, input [3:0] cnt_in, output reg [3:0] q); always @ (posedge clk) begin if(rst) q <= 4'b0000; else if(load) q <= cnt_in else if(down) q <= q - 1'b1; else q <= q + 1'b1; end endmodule
1 bit serial in serial out shift register. Propagation occurs on the rising edge of the clock.
- Order of assignment does not matter
module shiftreg(input clk, y, output reg q); req q1,q2,q3; always @ (posedge clk) begin q1 <= y; q2 <= q1; q3 <= q2; q <= q3; end endmodule
Can make the module simpler using vectors, where each stage in the shift register is a separate position in the vector. The LSB is replaced by the input, and the MSB is the output.
module shiftreg(input clk, y, output reg q_out); req [4:0] q; always @ (posedge clk) begin q <= y; q[4:1] <= q[3:0]; q_out <= q; end endmodule
- 64 element memory requires 6-bit address input, with each word as 16 bits
- Declare internal 64-element array, where each position is 16 bits.
module spram(input clk, en, write_en, input [5:0] addr, input [15:0] d_in output reg [15:0] d_out); reg [15:0] ram [0:63]; always @ (posedge clk) begin if (en) begin if (write_en) begin ram[addr] <= d_in; end d_out <= ram [addr]; end end endmodule
On each clock cycle:
- output 16 bit word that is on the provided addres
d_inis stored at the memory location