In VLSI industry Front-end digital design inlcudes RTL design, synthesis, optimization and verification. In the current repository i have included the deisgn, synthesis and optimization work on various digital modules, from very simple digital logic (mux, decoder, encoder etc.) to complex designs(barrel shifter, booth multiplier, dual port ram etc). All the work has been done using open source tools:-
- iverilog for RTL simulation.
- gtkwave for reading .vcd file and waveform generation.
- yosys for synthesis and netlist generation.
- skywater 130nm open source pdk.
With the introduction of open-source tools in the ASIC flow, now VLSI engineers have opportunity to design and craft their own ideas starting from RTL 2 GDSII. Moreover further inclusion of VLSI enthusiast and learners have propelled the need of open-source community for hardware also. Organizations such as:-
- open source hardware association
- SiFive
- FOSSI
- OpenPOWER Foundation
- VSD
- RISCV
- Redwood EDA
- Efabless etc
are committed to bring revolution in the Silicon industry.
I have categorized my work in day wise learning, according to the workshop conducted by VSD.
It's a way of writng hdl code for digital design in a way to exploit the behavioral features offered by the language and also utilizing the structural description for design accuracy. So it's a trade off between behavioral constructs and structural description while writing hardware code.
Translation and optimization of the RTL code into the gate level netlist. The synthesis tool(yosys) takes hdl code, liberty file(.lib) and design constraints to generate gate level netlist.
Representation of the design in terms of actual standard cells and their connections.
Liberty Timing file(.lib) consist of timing and power parameters associated with standard cells specific to a particular technology node. Generally it includes definition of an inverter, nand2, nand3, nand4, nor2, nor3, nor4, aoi12, aoi22, oai12, oai22, mux, dff etc.
NOTE
Why Standard Cell library includes various(in terms of PPA) types of gate cells?
In the digital circuit some combinational paths requires cells that are very fast in performance(but requires more area and power) and likewise some combinational paths requires cells that are slower in performance(but better in terms of area and power). So adhering to the timing conditions, which standard cells are to be used the decision is made. These decisions are written in constraint file and are passed to synthesis tool(yosys) for guided synthesis.
$ yosys
yosys>
yosys> read_liberty -lib ../../path_to_the_standard_library_file.lib // reading the liberty file.
yosys> read_verilog ../../path_to_verilog_file_that_is_to_be_synthesized.v // reading the verilog file.
yosys> synth -top module_name // the module name that is to be synthesized.
yosys> abc -liberty ../../path_to_to_liberty_file.lib // netlist generation.
yosys> show // a gui based representation of logic synthesized.
yosys> write_verilog file_name.v // write the generated netlist to new a module/verilog
// file for verification.
result of the command "yosys> synth -top module_name"
- result of the command "yosys> abc -liberty ../../path_to_liberty_file.lib" NOTE
- the difference in synthesis performed by various version of yosys.
- synthesis performed by yosys 0.7.
- logical representation of the deisgn by yosys 0.7.
- synthesis performed by yosys 0.9.
- logical representation of the design by yosys 0.9.
NOTE
the synthesis performed by yosys 0.7 is choosing different standard cells as compared to synthesis by yosys 0.9.
- netlist generated by yosys 0.7.
sky130_fd_sc_hd_tt_025C_1v80.lib
the liberty timing file(.lib) is an ASCII file consists of detailed information of timing and power parameters about any standard cell of a particular technology node. The parameters are affected by PVT variations (process, voltage, temperature). These variations are factorized while designing IC. So the Standard Cell library is characterized to model these variations. The feature size in this technology node is 130nm. The terms in the .lib file are:-
- fd - the skywater foundary
- sc - digital standard cell
- hd - high density
- tt - typical process
- 025C - temperature
- 1v - voltage
library in the sky130 pdk are named using the following scheme:
"" Process name _ Library Source Abbreviation _ Library Type Abbreviation _ Library Name ""
Library Source | Abbreviation |
---|---|
The SkyWater Foundary | fd |
Efabless | ef |
Oklahoma State University | osu |
Library Type | Abbreviation |
---|---|
Primitive Cells | pr |
Digital Standard Cells | sc |
Build Space(Flash, SRAM, etc) | sp |
IO and Periphery | io |
High Densiry | hd |
- sky130_fd_sc_hd - High Density Standard Cell Library.
- sky130_fd_sc_hdll - High Density, Low Leakage Standard Cell Library.
- sky130_fd_sc_hs - Low Voltage (<2.0V), High Speed, Standard Cell Library.
- sky130_fd_sc_ms - Low Voltage (<2.0V), Medium Speed, Standard Cell Library.
- sky130_fd_sc_ls - Low Voltage (<2.0V), Low Speed, Standard Cell Library.
- sky130_fd_sc_lp - Low Voltage (<2.0V), Low Power, Standard Cell Library.
- sky130_fd_sc_hvl - High Voltage (5V), Standard Cell Library.
more details can be found at skywater-pdf.
standard cell definition of and3_or1_invert:-
area comparison of different types of and cells.
hierarchical synthesis
the synthesis results obtained after hierarchical synthesis shows that the sub-modules and the hierarchy of it's instantiation is preserved. The design is partitioned into a much higher level of abstraction.
logical description of hierarchical synthesis
netlist of hierarchical synthesis
Note: in the Hierarchical Synthesis the design is partitioned into sub-modules hence it becomes easy to trace back each component of the design. Also the design complexity is less and easy to comprehend.
flat synthesis
$ yosys> flatten // to invoke flat synthesis after netlist generation
$ yosys> write_verilog -noattr multiple_modules_flat.v // to write verilog netlist without attributes(clean)
in the flat synthesis, instantiation of standard cells take place and the design is partitioned into a much lower level of abstraction.
logical description of flat synthesis
netlist comparison of flat synthesis vs hierarchical synthesis
$ yosys> synth -top sub_module1 // to synthesize only the submodule
- sub-module synthesis is preferred if:
- there are multiple instances of same module. So it's better to synthesize the module once and then use it multiple times.
- if desing and conquer approach is required i.e. if the design is very massive and complex, so it's better to synthesize module by module and then finally stitch together all the modules into the top module.
asynchronous reset dff
always@(posedge clk or posedge async_reset) begin
if(async_reset) q<= 1'b0;
else q<= d_in;
end
asynchronous set dff
always@(posedge clk or posedge async_set) begin
if(async_set) q<= 1'b1;
else q<= d_in;
end
asynchronous-synchronous reset dff
always@(posedge clk or posedge async_reset) begin
if(async_reset) q<= 1'b0;
else if(sync_reset) q<= 1'b0;
else q<= d_in;
end
synchronous reset dff
always@(posedge clk) beign
if(sync_reset) q<= 1'b0;
else q<= d_in;
end
Note
- Asynchronous reset pin is directly synthesized on the flip flop.
- Synchronous reset signal is passed through a combinational logic to the flip flop.
module mul2(
output wire y[3:0],
input wire in[2:0]
);
assign y= a * 2;
endmodule
// there is an interesting optimiztion taking place here instead of generating any hardware for the logic.
Note
- there are only wire components in the circuit. Also note the comment by the synthesis tool(yosys) while mapping.
module mul8(
output wire y[5:0],
input wire in[2:0]
);
assign y= a * 9;
endmodule
Note
- here replication of input takes place.
command to perform optimization
$ yosys > opt_clean -purge // all optimizations are done with this
// enter this command after "synth"
- case 1
module opt_check(
output wire y,
input wire a,
input wire b
);
assign y= a ? b : 0;
endmodule
- expected optimization by tool.
- result obtained.
- Case 2
module opt_check2(
output wire y,
input wire a,
input wire b
);
assign y= a ? 1 : b;
endmodule
- expected optimization by tool.
- result obtained.
- Case 3
module opt_check3(
output wire y,
input wire a,
input wire b,
input wire c
);
assign y= a ? ( C ? b : 0 ) : 0;
endmodule
- expected optmization by tool.
- result obtained.
- Case 4
module opt_check4(
output wire y,
input wire a,
input wire b,
input wire c
);
assign y= a ? ( b ? ( a & c ) : c ) : ( !c );
endmodule
- expected optimization by tool.
result obtained.
Note
before running optimization command, first flatten the design.
$ yosys > synth -top module_name // synthesizing the top module
$ yosys > flatten // flattening the entire design and removing the hierarchy
$ yosys > opt_clean -purge // running optimization
$ yosys > abc -liberty /path_to_.lib // mapping into the liberty file standard cells
$ yosys > write_verilog file.v // writing netlist
- Case 5
module sub_module1(input a , input b , output y);
assign y = a & b;
endmodule
module sub_module2(input a , input b , output y);
assign y = a^b;
endmodule
module multiple_module_opt(input a , input b , input c , input d , output y);
wire n1,n2,n3;
sub_module1 U1 (.a(a) , .b(1'b1) , .y(n1));
sub_module2 U2 (.a(n1), .b(1'b0) , .y(n2));
sub_module2 U3 (.a(b), .b(d) , .y(n3));
assign y = c | (b & n1);
endmodule
expected optimization by tool.
result obtained after optimization.
- Case 6
module sub_module(input a , input b , output y);
assign y = a & b;
endmodule
module multiple_module_opt2(input a , input b , input c , input d , output y);
wire n1,n2,n3;
sub_module U1 (.a(a) , .b(1'b0) , .y(n1));
sub_module U2 (.a(b), .b(c) , .y(n2));
sub_module U3 (.a(n2), .b(d) , .y(n3));
sub_module U4 (.a(n3), .b(n1) , .y(y));
endmodule
expected optimization by tool.
result obtained after optimization.
netlist of the optimized design.
- Case 1
module dff_const1(
output reg q,
input wire clk,
input wire reset
);
always @(posedge clk or posedge reset)
begin
if(reset)
q <= 1'b0;
else
q <= 1'b1;
end
endmodule
statistics after synth command.
result after optmization.
- Case 2
module dff_const2(
output reg q,
input wire clk,
input wire reset
);
always@(posdge clk or posedge reset) begin
if(reset) q<= 1'b1;
else q<= 1'b1;
end
endmodule
statistics after synth command.
result after optimization.
- Case 3
module dff_const3(
output reg q,
input wire clk,
input wire reset
);
reg q1;
always@(posedge clk or posedge reset) begin
if(reset) begin
q<= 1'b1;
q1<= 1'b0;
end
else begin
q1<= 1'b1;
q<= q1;
end
end
endmodule
statistics after synth command.
result after optimization.
- Case 4
module dff_const4(
output reg q,
input wire clk,
input wire reset
);
reg q1;
always@(posedge clk or posedge reset) begin
if(reset) begin
q<= 1'b1;
q1<= 1'b1;
end
else begin
q1<= 1'b1;
q<= q1;
end
end
endmodule
statistics after synth command.
result after optimization.
- Case 5
module dff_const5(
output reg q,
input wire clk,
input wire reset
);
reg q1;
always@(posedge clk or posedge reset) begin
if(reset) begin
q<= 1'b0;
q1<= 1'b0;
end
else begin
q1<= 1'b1;
q<= q1;
end
end
endmodule
statistics after synth command.
result after optimization.
- Case 1 : 3 bit up-counter
module counter_opt(
output reg q,
input wire clk,
input wire reset
);
reg [2:0] count;
assign q= count[0];
always@(posedge clk or posege reset) begin
if(reset) count< =3'b000;
else count<= count + 1;
end
endmodule
statistics after synth command.
result after optimization.
- Case 2 : 3 bit up-counter
module counter_opt(
output reg q,
wire clk,
wire reset
);
reg [2:0] count;
assign q= (count[2:0]== 3'b000);
always@(posedge clk or posege reset) begin
if(reset) count< =3'b000;
else count<= count + 1;
end
endmodule
statistics after synth command.
result after optimization.
Gate level simulation
using netlist generated by the synthesis tool as DUT and test bench(same test bench i.e. used for RTL simulation) the simulation tool iverilog can perform a logical simulation called as Gate level simulation. It is performed to verify the logical correctness of the design after synthesis.
- case 1:
assign y= sel ? i1 : i0; // infers a 2:1 mux
statistics after synth command.
logical description.
RTL simulation.
Gate level simulation.
- Case 2:
always@(sel) begin
if(sel) y<= i1;
else y<= i0;
end
statistics after synth command.
RTL simulation.
Gate level simulation.
module blocking_caveat(
output reg d,
input wire a,
input wire b,
input wire c
);
reg x;
always @(*) begin
d= x & c;
x= a | b;
end
endmodule
statistics after synth command.
RTL simulation.
Gate level simulation.
-
case 1:
module incomp_if( output reg y, input wire i0, input wire i1, input wire i2, );
always@(*) begin if(i0) y<= i1; end
endmodule
statistics after synth command.
logical description.
RTL simulation.
-
case 2:
module incomp_if2( output reg y, input wire i0, input wire i1, input wire i2, input wire i3 ); always@(*) begin if(i0) y<= i1; else if y<= i3; end endmodule
statistics after synth command.
logical description.
RTL simulation.
module incomp_case(
output reg y,
input wire i0,
input wire i1,
input wire i2,
input wire [1:0] sel
);
always@(*) begin
case(sel)
2'b00: y= i0;
2'b01: y= i1;
endcase
end
endmodule
statistics after synth command.
logical description.
RTL simulation.
module comp_case(
output reg y,
input wire i0,
input wire i1,
input wire i2,
input wire [1:0] sel
);
always@(*) begin
case(sel)
2'b00: y= i0;
2'b01: y= i1;
default: y= i2;
endcase
end
endmodule
statistics after synth command.
logical description.
RTL simulation.
module partial_case_assign(
output reg y,
output reg x,
input wire i0,
input wire i1,
input wire i2,
input wire [1:0] sel
);
always@(*) begin
case(sel)
2'b00: begin
y= i0;
x= i2;
end
2'b01: y= i1;
default: begin
x= i1;
y= i2;
end
endcase
end
endmodule
statistics after synth command.
logical description.
module bad_case(
output reg y,
input wire i0,
input wire i1,
input wire i2,
input wire i3,
input wire [1:0] sel
);
always@(*) begin
case(sel)
2'b00: y= i0;
2'b01: y= i1;
2'b10: y= i2;
2'b1?: y= i3;
endcase
end
enmodule
statistics after synth command.
logical description.
RTL simulaiton
module mux_generate(
output reg y,
input wire i0,
input wire i1,
input wire i2,
input wire i3,
input wire [1:0] sel
);
wire [3:0] i_int;
assign i_int= {i3, i2, i1, i0};
intege k;
always@(*) begin
for(k=0; k< 4; k= k+1) begin
if(k == sel) y= i_int[k];
end
end
endmodule
statistics after synth command.
logical description.
RTl simulation.
module demux_case(
output wire o0,
output wire o1,
output wire o2,
output wire o3,
output wire o4,
output wire o5,
output wire o6,
output wire o7,
input wire i
);
reg [7:0] y_int;
assign {o7, o6, o5, o4, o3, o2, o1, o0}= y_int;
intege k;
always@(*) begin
y_int= 8'b0;
case(sel)
3'b000: y_int[0]= i;
3'b001: y_int[1]= i;
3'b010: y_int[2]= i;
3'b011: y_int[3]= i;
3'b100: y_int[4]= i;
3'b101: y_int[5]= i;
3'b110: y_int[6]= i;
3'b111: y_int[7]= i;
endcase
end
endmodule
statistics after synth command.
logical description.
RTL simulation.
Gate level simulation.
module fa(
output wire sum,
output wire co,
input wire a,
input wire b,
input wire c,
);
assign {co, sum}= a + b + c;
endmodule
module rca(
output [8:0] sum,
input wire [7:0] num1,
input wire [7:0] num2,
);
wire [7:0] int_sum;
wire [7:0] int_co;
genvar i;
generate
for(i= 1: i<8; i= i+1) begin
fa u_fa_1(
.a(num1[i]),
.b(num2[i]),
.c(int_co[i-1]),
.sum(int_sum[i])
);
end
endgenerate
fa u_fa_0 (
.a(num1[0]),
.b(num2[0]),
.c(1'b0),
.co(int_co[0]),
.sum(int_sum[0])
);
assign sum[7:0] = int_sum;
assign sum[8]= int_co[7];
endmodule
statistics after synth command.
logical description.
RTL simulation.
Gate level simulation.