Introduction to FPGA
Development
Justin Thiel
Two Sigma Investments
Mapping Operations to HW
always @(posedge clk) begin
r_x <= x;
if (x)
r_y <= r_x;
end
x
clk
Flip
Flop
r_x
Flip
Flop
r_y
What is Inside an FPGA?
What is Inside an FPGA?
a + b = c
a
b
c
Carry
Out
0
0
0
0
0
1
1
0
1
0
1
0
1
1
0
1
LUT Inputs LUT Outputs
(Address)
(Data)
What is Inside an FPGA?
module ttl74163
#(
parameter WIDTH = 4
)
(
input clk,
input rst_n,
input [WIDTH-1:0] i_load_cnt,
input i_load_n,
input i_cnt_en,
input i_cnt_en_w_carry_out,
output [WIDTH-1:0] o_cnt,
output o_carry_out
);
// Some sweet logic here
...
endmodule
Exercise 1
74163 Binary Counter Simulation
Verilog Primer
Verilog Numerical Types
Format: [WIDTH]’[BASE][VALUE]
2’d0
2’b0
4’he
4’o7
-> 2 bit decimal 0
-> 2 bit binary 0
-> 4 bit hexadecimal 14
-> 4 bit octal 7
• Arbitary bit widths are
allowed
• Hex (h), Binary (b),
Decimal (d), and octal (o)
bases are handled
• Same numeric form is
used for everything (no
floats, doubles, etc..)
• For “signed” integers
logic tends to use 2s
complement format
Verilog Operators
Bitwise:
OR:
AND:
XOR:
NOT:
3’b010 | 3’b100 -> 3’h110
3’b100 & 3’b111 -> 3’b100
3’b101 ^ 3’b110 -> 3’b011
~3’b101
-> 3’b010
Arithmetic:
Addition
: 3’b010 + 3’b100 -> 3’b110
Subtraction: 3’b100 – 3’b001 -> 3’b011
Comparators:
Greater Than: (3’b100 > 3’b011) -> 1’b1
Equality
: (3’b100 == 3’b010) -> 1’b0
Inequality : (3’b100 != 3’b000) -> 1’b1
Verilog Concepts
wire w_rst;
assign w_rst = ~rst_n;
clk
rst_n
w_rst
• wires are used to define
“stateless” signals
• They can only be
assigned to by a (single)
assign statement
• The value of a wire
changes whenever the
inputs to it’s assign
statement transition
rst_n
w_rst
Verilog Concepts
reg r_en_a;
always@(posedge clk) begin
r_en_a <= i_en_a;
end
clk
• regs are used to define
“stateful” signals.
• They should only be
assigned to within a
(single) always block
• The value of a reg
changes only when its
always block evaluates
i_en_a
Flip
Flop
i_en_a
r_en_a
clk
r_en_a
Verilog Concepts
reg r_en_a;
reg r_en_b;
always@(posedge clk) begin
r_en_a <= i_en_a;
r_en_b <= r_en_a;
end
clk
i_en_a
r_en_a
r_en_b
• All statements in an
always block evaluate in
parallel.
i_en_a
Flip
Flop
clk
r_en_a
Flip
Flop
clk
r_en_b
Verilog Concepts
reg r_en_a;
reg r_en_b;
• All always blocks
execute in parallel.
always@(posedge clk) begin
r_en_a <= i_en_a;
end
always@(posedge clk) begin
r_en_b <= r_en_a;
end
clk
i_en_a
r_en_a
r_en_b
i_en_a
Flip
Flop
clk
r_en_a
Flip
Flop
clk
r_en_b
Verilog Concepts
reg r_rst;
always@(*) begin
r_rst <= ~rst_n;
End
..Same as...
always@(rst_n) begin
r_rst <= ~rst_n;
end
clk
rst_n
r_rst
• always blocks are not
required to be clocked
• Making a block sensitive
to ‘*’ causes it to
evaluate whenever a
right-hand side value
changes
rst_n
r_rst
Dealing with Bits
// 1 bit wire
Wire w_rst;
// 4 bit register
reg [3:0] r_cnt;
// 1024 bit wire
wire [1023:0] x;
// 4x 32b registers
reg [31:0] r_data [3:0];
// 4x4x 32b registers
reg [31:0] r_data2d [3:0][3:0];
• Brackets ‘[]’ are used to
denote multi-bit fields
Dealing with Bits
• Brackets ‘[…]’ are also
used to bit slice vectors.
• Use ‘:’ to specify a range.
wire [3:0] x;
wire y;
wire [1:0] z;
// Set y to Bit 0 of x
assign y = x[0];
// Set z to Bits 1:0 of x
assign z = x[1:0];
x[1]
x[0]
y
z[0]
z[1]
Dealing with Bits
• Brackets ‘[…]’ are also
used to index into arrays
wire [3:0] x [1:0];
wire [3:0] y;
wire [3:0] z;
// Pulling Array Entries
assign x[0] = y;
assign x[1] = z;
z
y
x[0]
x[1]
Dealing with Bits
• Use curly braces ‘{…}’ to
concatenate bit vectors
wire [1:0] x;
wire y;
wire z;
// Set x[1] to y, x[0] to z of
y and z
assign x = {y,z};
z
y
x[1]
x[0]
Module Declaration and Port Maps
module ttl74163
#(
parameter WIDTH = 4
)
(
// Clock and Reset Pins
input clk,
input rst_n,
// Inputs
input [WIDTH-1:0] i_load_cnt,
input i_load_n,
...
// Outputs
output [WIDTH-1:0] o_cnt,
output o_carry_out
);
• All designs start with a
module declaration
Module Declaration and Port Maps
module ttl74163
#(
parameter WIDTH = 4
)
(
// Clock and Reset Pins
input clk,
input rst_n,
// Inputs
input [WIDTH-1:0] i_load_cnt,
input i_load_n,
...
// Outputs
output [WIDTH-1:0] o_cnt,
output o_carry_out
);
• parameters can be
used to create logic
which is configurable at
compile (synthesis) time
• They must be enclosed
within a “#( … )” block
Module Declaration and Port Maps
module ttl74163
#(
parameter WIDTH = 4
)
(
// Clock and Reset Pins
input clk,
input rst_n,
// Inputs
input [WIDTH-1:0] i_load_cnt,
input i_load_n,
...
// Outputs
output [WIDTH-1:0] o_cnt,
output o_carry_out
);
• Inputs denote signals
which are driven outside
the module
• Outputs denote signals
which are driven by the
module
• These signals form the
portmap and are
enclosed in a “(…)” block
i_load_cnt
i_load_n
o_cnt
ttl74163
o_carry_out
Module Instantiation
parameter MY_WIDTH = 4;
wire
wire
wire
wire
[MY_WIDTH-1:0] w_load_cnt;
w_load_n;
[MY_WIDTH-1:0] w_cnt;
w_carry_out;
ttl74163
#( .WIDTH (MY_WIDTH) )
my_ttl_counter
( .clk (clk),
.rst_n (rst_n),
• Inputs and Outputs
should be connected to
wires/regs.
• Each instance
represents a unique copy
of the logic defined in the
module
• a given module is a
unique copy of the logic
.i_load_cnt (w_load_cnt),
.i_load_n (w_load_n),
w_load_cnt
...
.o_cnt (w_cnt),
w_load_n
.o_carry_out (w_carry_out));
w_cnt
my_ttl_counter
w_carry_out
Verilog Constructs
reg [3:0] r_cnt;
wire [3:0] w_cnt;
always@(posedge clk) begin
// Clear
if (rst)
r_cnt <= '0;
else
r_cnt <= w_cnt;
end
4’d0
w_cnt
1
• Simple if..else
blocks synthesize as a 2to-1-mux
• This construct can only
be used in an always
block.
0
Flip
Flop
rst
clk
r_cnt
Verilog Constructs
reg r_set;
always@(posedge clk) begin
r_set <= (rst) ? 0 : i_set;
end
...
• Ternary operators (?)
also imply a 2-to-1 mux
• Can be used in always
blocks or assign
statements.
wire w_set;
assign w_set =
(rst) ? 0 : i_set;
1’b0
i_set
1
w_set
0
Flip
Flop
rst
clk
r_set
Verilog Constructs
reg [1:0] r_cnt; wire w_a, w_b;
always@(posedge clk) begin
case ({w_a, w_b}):
2’b01:
r_cnt <= 2’d1;
2’b10:
r_cnt <= 2’d1;
2’b00:
r_cnt <= 2’d0;
2’b11:
r_cnt <= 2’d2;
default:
r_cnt <= 2’d0;
endcase
end
2’b00
2’b01
2’b10
2’b11
{w_a, w_b}
• case statements will
(generally) imply an n-to1 mux
• This construct can only
be used in an always
block.
• default case is a
failsafe, provide for best
results
Flip
Flop
clk
r_cnt
Verilog Constructs
• Arithmetic operations
synthesize into physical
representations thereof
reg [1:0] r_cnt;
wire w_en;
always@(posedge clk) begin
if (w_en)
r_cnt <= r_cnt + 1;
else
r_cnt <= r_cnt;
end
0
2’d1
2 bit
Adder
1
w_en
2x
Flip
Flops
clk
r_cnt
Verilog Constructs
// Full Adder Example
module full_adder(
input i_a,
input i_b,
input i_cin,
output o_sum,
output o_cout
);
wire w_tmp;
assign w_tmp = (i_a ^ i_b);
assign o_sum = (w_tmp ^ i_cin);
assign o_cout=
(i_a & i_b) | (i_cin & w_tmp);
endmodule;
• You can also build
arithmetic operators
yourself…
a
b
Ci
S
Co
0
0
0
0
0
0
0
1
1
0
0
1
0
1
0
0
1
1
0
1
1
0
0
1
0
1
0
1
0
1
1
1
0
0
1
1
1
1
1
1
Verilog Constructs
reg [3:0] r_cnt;
always@(posedge clk) begin
// Clear
if (w_rst)
r_cnt <= '0;
// Load value
else if (w_load)
r_cnt <= i_cnt;
// Count
else if (w_cnt_en)
r_cnt <= r_cnt + 1;
else
r_cnt <= r_cnt;
end
• if..else if...else
blocks imply priority just
as they do in a C/C++.
• In this case, w_rst takes
priority, followed by
w_load and so on…
Verilog Constructs
else if (w_cnt_en)
r_cnt <= r_cnt + 1;
else
r_cnt <= r_cnt;
end
always@(posedge clk) begin
if (w_rst)
r_cnt <= '0;
else if (w_load)
r_cnt <= i_cnt;
...
4’d1
4 bit
Full
Adder
0
1
w_cnt_en
0
i_cnt
1
w_load
0
4’d0
1
w_rst
4x
Flip r_cnt
Flops
clk
Syntactic Sugar
wire [3:0] x;
wire [3:0] y;
wire [3:0] z;
assign x = ‘0;
assign y = ‘1;
assign z = (x == ‘1) ? x : y;
• ‘0 = “all bits set to zero”
• ‘1 = “all bits set to one”
Summary
• Use wires for purely combinatorial logic and regs for
anything stateful
• A reg does not always imply a flip flop
• Simple If…Else and Ternary statements synthesize into
2-to-1 muxes
• Chains of If…Else If…Else blocks can lead to long
timing paths
• A case statement will yield an n-to-1 mux when coded
properly
Questions
Exercise 2
Binary Counter Modification
Exercise 2 - Hint
clk
i_en
o_cnt
0
1
2
… 14 15 14 13 …
1
0
1
…
clk
Wrong
r_cnt_dn
o_cnt
0
1
2
… 14 15 0 15 14 13 12 11 …
clk
Correct
r_cnt_dn
o_cnt
0
1
2
… 14 15 14 13 …
1
0
1
…
Exercise 3
FPGA build flow
© Copyright 2026 Paperzz