Download presentation
Presentation is loading. Please wait.
1
SystemVerilog Implementation of GCD
Three modules (“design sources”) gcd_core.sv dp.sv fsm.sv Testbench (tb.sv) added later as a “simulation” source Then clk_def.xdc as a “constraint” source
2
///////////////////////////////////////////////////////////
`timescale 1ns / 1ps /////////////////////////////////////////////////////////// // Company: // Engineer: […] module gcd_core( input logic clk, input logic rst, input logic load, input logic [7:0] din, output logic [7:0] gcd_rslt, output logic done ); endmodule Using Xilinx “add sources” I selected “SystemVerilog Module”. SystemVerilog is case-sensitive Avoid name potential name conflicts (e.g., module gcd and output gcd) or keywords (e.g., output) Xilinx limitations: don’t use case to differentiate names; filename must match module name. Don’t forget bus range! timescale only used if modeling delays, e.g. in testbenches (Delays are in units of ns, but simulator resolution is 1 ps)
3
Unfortunately, HDL Instantiation Template is now a TCL app!
Structural Model Under the old ISE I followed this sequence: Next, created black-box models of the datapath and the controller Used “View HDL Instantiation Template” process under the “Design Utilities” category Instantiated the datapath and controller in the top module Added internal wires for connecting I found the TCL app for generating a component instantiation a bit tough to use, but perhaps it has improved. If you go to Tools > Xilinx Tcl Store you can see a list. The “write_template” utility is part of the Design Utilities package. Unfortunately, HDL Instantiation Template is now a TCL app!
4
[…] module gcd_core( […] ); // internal signals
logic xsel, xload, ysel, yload; logic sub_sel, x_eq_y, x_gt_y; // Instantiate the datapath – could have used .name or .* dp dp1 ( .clk(clk), .xsel(xsel), .xload(xload), .ysel(ysel), .yload(yload), .sub_sel(sub_sel), .din(din), .x_eq_y(x_eq_y), .x_gt_y(x_gt_y), .gcd_rslt(gcd_rslt) ); […] This is named association for connecting signals to the component. There are some shortcuts using “wildcards” in the standard (.name and .*) Note the internal signals representing the control and status signals between the FSM and the Datapath.
5
[…] // Instantiate the controller fsm fsm1 ( .clk(clk), .rst(rst), .load(load), .x_eq_y(x_eq_y), .x_gt_y(x_gt_y), .xsel(xsel), .xload(xload), .ysel(ysel), .yload(yload), .sub_sel(sub_sel), .done(done) ); endmodule
6
Modeling Logic Continuous assignments (with operators)
Module or primitive instantiations (structural) Procedural assignments (~ VHDL process) Blocking assignments (=) Best for combinational logic (~ VHDL variables) Non-Blocking assignments (<=) Best for clocked logic (~ VHDL signals) A good Rule of the Road: Don’t mix-n-match blocking and non-blocking assignments in a procedure. Instead, use one or more procedures for modeling pure combinational logic”, and separate ones for “clocked” logic.
7
Use “logic” for everything!!!
Datapath (dp.sv) Comparators, muxes, subtrator, registers Mix of continuous assignments and procedural assignments Continuous assignments require type “wire” Procedural assignments require a type “reg” The “reg” identifier does not imply register! Use “logic” for everything!!! This is one big improvement of SystemVerilog. “Logic” is a 4-value data type, similar to std_logic, but only with values of 0, 1, Z, and X.
8
assign x_eq_y = (x == y); assign x_gt_y = (x >= y);
module dp( […] ); logic [7:0] diff; logic [7:0] x, y; // comparators assign x_eq_y = (x == y); assign x_gt_y = (x >= y); // subtractor and muxes assign diff = (sub_sel ? y : x) - (sub_sel ? x : y); Watch width of nets – if not specified it is 1-bit. X and Y are the register outputs and diff is the subtractor output. C-style ternary operator useful for 2-1 mux “==“ returns 0 for false and 1 for true (unlike VHDL which used a type Boolean) There are other ways to model behavior of diff.
9
// x and y registers with cascaded muxes always_ff @(posedge clk)
[...] // x and y registers with cascaded muxes clk) begin if (xload) if (xsel) // could have used ternary operator x <= din; else x <= diff; if (yload) if (ysel) y <= din; else y <= diff; end assign gcd_rslt = x; endmodule Use non-blocking (<=) in a clocked procedure “else” goes with previous “if” Can add begin/end to control grouping Avoid complicated if-else nesting No final else for if(_load) implies memory (register). Note the use of “non-blocking” assignment. The execution of this procedural block is similar to that in VHDL, whereas the next statements are evaluated before the previous assignments take place, i.e., they are not blocked waiting for the assignment. This allows the model to capture the “parallel” nature of the registers. “always” implies it runs more than once (will see others in testbench) Final assign just a renaming of net for output port
10
Controller (fsm.sv) Moore FSM with unregistered outputs Two procedures
Next state logic and state register (“clocked”) Output logic (combinational) Use non-blocking for clocked logic Use blocking for combinational
11
// symbolic state names as an enumerated type typedef enum logic [2:0]
module fsm( […] ); // symbolic state names as an enumerated type typedef enum logic [2:0] {idle, loadx, loady, wait1, y_diff, x_diff, fine} statetype; statetype state; // NSL and state register - non-blocking assignment clk) begin if (rst) state <= idle; else case (state) idle : if (load) state <= loadx; loadx : state <= loady; wait1 : case ({x_eq_y, x_gt_y}) 2'b10, 2'b11 : state <= fine; 2'b00 : state <= y_diff; 2'b01 : state <= x_diff; endcase fine : state <= idle; default : state <= wait1; end […] Unspecified next state remains in the same state due to register Make sure the statetype is wide enough to accommodate the number of states.
12
“Default” outputs avoid “latches”
// output logic - blocking assignments always_comb begin // default outputs xload = 0; xsel = 0; yload = 0; ysel = 0; sub_sel = 0; done = 0; case (state) loadx : xload = 1; xsel = 1; end loady : yload = 1; ysel = 1; y_diff : yload = 1; sub_sel = 1; x_diff : xload = 1; fine : done = 1; endcase endmodule Default outputs prevent inferred latches. Note the use of begin/end under each case; otherwise only one statement allowed. Unlike C, comparison does not continue after a match, so no break statement required. “Default” outputs avoid “latches”
13
Test Fixture (Testbench)
Much more like programming! =:0 Generate clock and reset Apply stimulus Check results (optional) Terminate simulation (recommended)
14
// Instantiate the Unit Under Test (UUT) gcd_core uut ( .clk(clk),
`timescale 1ns / 1ps module tb; // Inputs logic clk; logic rst; logic load; logic [7:0] din; // Outputs logic [7:0] gcd_rslt; logic done; // Instantiate the Unit Under Test (UUT) gcd_core uut ( .clk(clk), .rst(rst), .load(load), .din(din), .gcd_rslt(gcd_rslt), .done(done) ); [...] Note: The tb module has no inputs or outputs on its interface Only a single component instantiation Internal signals correspond to UUT interface time scale means the unit multiplier for delays is ns with a resolution of 1 ps, so you could delay 1.001 ns.
15
Clock Generation [...] parameter CLK_PRD = 100; // 10 MHz clock
parameter HOLD_TIME = (CLK_PRD*0.3); initial begin clk <= 0; forever #(CLK_PRD/2) clk = ~clk; end Define the clock period and hold time requirements. The “initial” keyword identifies a single-pass behavior that will only “execute” once. In this case it generates a periodic waveform. Forever loop with a single statement The “#” is a delay construct that allows simulation time to advance before execution continues Basically, after specified delay (in nsec), execute the statement. Note the mix of blocking and non-blocking. Not recommended for synthesis, only testbenches Initial assignment with non-blocking is to allow other initial blocks to evaluate at start-up (I think!)
16
Reset and Stimulus Alignment
initial begin // Initialize Inputs rst = 0; load = 0; din = 8'bx; // Wait 100 ns for global reset to finish #100; // Add stimulus here @(posedge clk); // align with clock edge #HOLD_TIME; // offset a hold time repeat(2) #CLK_PRD; // Now only wait integer clock periods rst = 1; #CLK_PRD; repeat(2) #CLK_PRD; [...] FPGA include a global set/reset after configuration that is modeled in post-implementation simulation, so you want your testbench to wait. @ is an event control operator with a sensitivity list Followed by #hold sets stimulus at a hold time after rising edge. din set to all “x” clock twice to allow X to propagate, then reset for 1 cc, then clock two more cycles to make sure it stays initialized
17
Stimulus and Done Check
[...] load = 1; #CLK_PRD; load = 0; din = 8'd27; #CLK_PRD; din = 8'd18; #CLK_PRD; din = 8'bx; #CLK_PRD; begin : run_loop forever begin @(posedge clk); if (done) disable run_loop; end end // run_loop $finish; endmodule din set back to “x” Named forever loop to allow break on done $finish is a system task that returns control to the simulator Will run forever if done never asserted, causing simulator to appear to lock up Better is to include a max sim constraint using a single cycle block
18
Simulation Termination
parameter MAX_SIM_TIME = (100*CLK_PRD); initial #(MAX_SIM_TIME) $finish; Allows a maximum simulation time of 100 clock cycles unless “done” occurs first
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.