Please Log In for full access to the web site.
Note that this link will take you to an external site (https://shimmer.mit.edu) to authenticate, and then you will be redirected back to this page.
Error on line 5 of Python tag (line 6 of source): kerberos = cs_user_info['username'] KeyError: 'username'
lab-bc build tool now or in the future. SSH Key Submissions may take up to 24 hours to take effect, so do this early!You need to do two Lab Safety Things for 6.111:
- Please go to this link and take the EHS00509w Electrical Safety Awareness course. To do this, search for "509" under the Course Catalog Page, which should find the "Electrical Safety Awareness" course. Do this course. It isn't too bad.
- Please go to this link and read and sign the undergraduate lab safety acknowledgement form
We're going to build two simple FPGA designs today. These should be relatively quick to synthesize since they are comprised of combinational-only logic. We will go through the whole build process and discuss how to set up your file tree for a design. Be aware: We are doing this in a different way than we have in the past, and in a way that is not often discussed online. However, doing it this way will:
- Make the build quicker
- Work in a manner closer to how you would in industry
- Avoid using Vivado's limited GUI infrastructure
- Allow you to use whatever code editor you're most comfortable with
- Let you keep track of files in a version-control-friendly way (if you so desire)
The Project Folder
So let's get started! On your computer make a new directory and call it lab01. In that folder, make the following subdirectories called:
src: This will contain.svand.vfiles intended for synthesis. This is the stuff that's actually going onto your Nexys board, to program it and make it do interesting things.sim: This will contain.svand.vfiles intended for simulation. You'll learn more about these, how they differ from proper source files, and how we use them in a bit.xdc: This will contain the constraints file for sythesis. We'll explain what this is later, but there is usually only one of these, and it has a .xdc extension.obj: This will contain output products and information from Vivado. This includes the bit file, which is eventually written to the FPGA.
SV Files
These are your (System) Verilog files. They have an ".sv" extension on them to inform Vivado (and other software) that they use the SystemVerilog standard. Occasionally we'll also use regular ".v" files for particular purposes. The purpose of these files can be divided into two categories:
- Synthesis Files: These files describe hardware designs. These eventually get turned into actual circuits onboard the FPGA. As mentioned previously, we place them in the
srcdirectory. - Simulation Files: These files, often called "test benches" are not meant to be synthesized. Instead, they are used to test other Verilog files. These files will live in the
simdirectory. It is best to keep them separate so that Vivado doesn't try to synthesize non-synthesizable logic.
Why simulation? Since Verilog takes a long time to build for FPGAs, using test benches to simulate modules off-FPGA dramatically reduces development time. When writing software (ie, your 6.101/6.009 lab) your code executes quickly, and writing tests might feel like a waste of time. Since we're writing code to describe hardware, not software, our code takes way longer to build, making simulations significantly more valuable.
XDC Files
An FPGA is a chip. That chip has a bunch of metal pins on it that are named with a letter-number grid scheme like "H17" or "K15". We use an XDC file to apply settings to these pins, as well as to give them more meaningful names. For example, pin H17 is hardwired to one of the LEDs on this board, so we call it led[0] in the XDC file.
A default XDC file top_level.xdc is provided for you here. By default ALL lines are commented out. At the start of any project, uncomment the lines used as inputs/outputs in your top_level module, which serves as the entry point for your entire design. Go ahead and put a copy of this file into your xdc directory. We'll come back to it in a moment.
Output Files
When you build a design with Vivado it outputs a .bit file that configures the FPGA's internals to match your design. It also generates a number of reports about resources used, timing issues, and other things. Much of the time the pot of gold at the end of the rainbow is the .bit file, though the reports can be really helpful too. For now this directory is empty since we haven't run Vivado yet.
For a first project, we're going to use the bto7s module you wrote in PSet 01 to control the seven-segment display on the Nexys board. It won't control all the digits independently (we'll need sequential logic for that) so each digit on the display will show the same thing. We'll use two .sv files for this: one to contain the bto7s module, and one to connect the module to the physical pins on the chip.
Make both files, and copy in your working bto7s module from PSet 01 into bto7s.sv. The contents of top_level.sv are below:
// prevents system from inferring an undeclared logic (good practice)
`default_nettype none
module top_level(
input wire [15:0] sw,
input wire btnl,
input wire btnu,
input wire btnr,
output logic[15:0] led,
output logic led17_r,
output logic led16_b,
output logic [7:0] an,
output logic ca,cb,cc,cd,ce,cf,cg);
logic [6:0] cat_segs;
// instantiate a bto7s module called 'converter'
bto7s converter(.x_in(sw[3:0]), .s_out(cat_segs));
// a typo...keep this here for moment
assign {cg,cf,ce,cd,cc,cb,ca} = ~cat_segss;
assign an = 8'b0;
/* we'll use the LEDs later...for now, just link them to the switches
* and force some lights on
*/
assign led = sw;
assign led17_r = 1'b1;
assign led16_b = 1'b1;
endmodule // top_level
/* I usually add a comment to associate my endmodule line with the module name
* this helps when if you have multiple module definitions in a file
*/
// reset the default net type to wire, sometimes other code expects this.
`default_nettype wire
This file is the top level file and its inputs and outputs are actually going to be the pins on the FPGA, which are connected to the peripherals on the development board. Specifically, the module above is dropping the bto7s module into the following sort of schematic:
The missing "link" right now between our top_level module and the actual FPGA. We've declared an output called led that is 16 bits wide, but the FPGA doesn't know what that maps to on the actual chip. The XDC file provides this mapping. We only want to include mappings for connections we actually use, so we'll only uncomment the lines needed to setup the top_level module with its inputs and outputs. XDC files use "#" comments so we just remove the leading # on the necessary lines. Specifially:
For example to activate sw[0] you'd simply start with this:
##Switches
#set_property -dict { PACKAGE_PIN J15 IOSTANDARD LVCMOS33 } [get_ports { sw[0] }]; #IO_L24N_T3_RS0_15 Sch=sw[0]
#set_property -dict { PACKAGE_PIN L16 IOSTANDARD LVCMOS33 } [get_ports { sw[1] }]; #IO_L3N_T0_DQS_EMCCLK_14 Sch=sw[1]
#set_property -dict { PACKAGE_PIN M13 IOSTANDARD LVCMOS33 } [get_ports { sw[2] }]; #IO_L6N_T0_D08_VREF_14 Sch=sw[2]
and then go to this:
##Switches
set_property -dict { PACKAGE_PIN J15 IOSTANDARD LVCMOS33 } [get_ports { sw[0] }]; #IO_L24N_T3_RS0_15 Sch=sw[0]
#set_property -dict { PACKAGE_PIN L16 IOSTANDARD LVCMOS33 } [get_ports { sw[1] }]; #IO_L3N_T0_DQS_EMCCLK_14 Sch=sw[1]
#set_property -dict { PACKAGE_PIN M13 IOSTANDARD LVCMOS33 } [get_ports { sw[2] }]; #IO_L6N_T0_D08_VREF_14 Sch=sw[2]
For our design, we want to use the following connections on the chip:
- All 16
swpins. - The
btnl,btnu, andbtnrbuttons. - All 16
ledpins. - All 8
ananode pins of the seven-segment display. - The
ca,cb,cc,cd,ce,cf, andcgcathode pins of the seven-segment display. - The red and blue channels of LEDs seventeen and sixteen, which Digilent engineers decided to bequeath upon the titles of
led17_randled16_b. We thank them for their deeds. 1
Build
port = 12345 to port = 80, then try again. We initially planned to use port 12345 to send data to our build machines, but a firewall present at the boundary of the MIT SECURE network blocks this... Using your preferred Vivado setup, generate a bitstream for this design:
- If you installed Vivado locally, do
vivado -mode batch -source build.tcl, and watch the messages scroll by.-mode batchforces Vivado to build on the command line, and-source build.tclforces Vivado to use your build script rather than defaulting to a 'project-mode' setup. Here is a copy of a pretty generic build.tcl that you should put in the root of your lab folder. Both of these save you a ton of time - get used to using them! If all goes well, this should succeed, and you'll wind up with an output file inobj. - If you are running Vivado on a lab machine, do the same as above.
- If you're using
lab-bc, droplab-bc.pyinto your project directory and runpython3 lab-bc.py -o objand wait a while. The same thing should happen, and you'll also wind up with a build log inobj.
You'll notice something broke, no matter which build method you used. Read the lines on your screen if you used option 1 or 2, and you'll notice an error message in obj/build.log if you used lab-bc. It turns out there's a typo in your Verilog code (attentive readers may have noticed we commented it above...). Fix this typo and rebuild -- now you know what an error message looks like.
Once this is done, using openFPGALoader, flash the output bitfile (obj/out.bit) to your FPGA. Verify that the lower four bits can set all sixteen appropriate digits on the displays like shown in the video below.
For checkoff 1, show your seven segment display working with the lower four switches.
ok for the second part of this lab we'll implement a ALU (Arithmetic Logic Unit).
The ALU will take in two 8 bit numbers:
- Number 0 (
n0) is taken from the lower eight bits of the switch array (sw[7:0]) - Number 1 (
n1) is taken from the upper eight bits of the switch array (sw[15:8])
The buttons btnr, btnu, and btnl will be used together to select from one of eight operations:
The buttons {btnl, btnu, btnr} will be used to select from one of eight operations:
- (
000): Addition:n1 + n0 - (
001): Subtraction:n1 - n0 - (
010): Multiplication:n1 * n0 - (
011): Division:n1 / n0(integer division) - (
100): Remainder:n1 % n0(modulo) - (
101): Bitwise AND:n1 & n0 - (
110): Bitwise OR:n1 | n0 - (
111): Bitwise XOR:n1 ^ n0
In addition two other checks will always be performed:
- Equality:
n1 == n0with its result presented onled16_b - Greater than:
n1 > n0with its result presented onled17_r
Below we've provided a basic testbench for you to locally develop and simulate this ALU. You might say, "I don't need to do a testbench or simulate, I'll just code this up and iMmEdIaTeLy run a Vivado build." These are famous last words. Many an all-nighter has begun with similar thoughts. Test benching/simulating your code sucks because you have to write more code, but it sucks way less than the making miniscule changes and then waiting hours for a hardware build to happen.
A testbench file is still Verilog, but it is not synthesizable Verilog. It is meant for simulation. Consequentely a testbench file should be thought of more as a regular program file that "runs". Starts at the top and goes in order as you go down. There are still rules and things, of course, but it should feel more natural to you coming from a Python/C existence in terms of its ordering/causality.
One of the great difficulties in Verilog is that it was originally meant to be a simulation language and was then bent into the Hardware Description Language role. It isn't the end of the world, but always try to keep track of the two types of files:
- Synthesizable Verilog (files that we use to describe hardware). You will never see any sort of "time" in synthesizable Verilog
- Simulation Verilog (testbenches). This Verilog will have a concept of time, and is meant to run and test in simulation synthesizable Verilog files.
// set the timestep on the internal simulation clock
`timescale 1ns / 1ps
`default_nettype none
//The timescale specifies the timestep size (1ns) and time resolution of rounding (1ps)
//we'll usually use 1ns/1ps in our class
module alu_tb();
//make inputs and outputs of appropriate size for the module testing:
logic [7:0] d0_in;
logic [7:0] d1_in;
logic [2:0] sel_in;
logic [15:0] res_out;
logic gt_out;
logic eq_out;
//create an instance of the module. UUT= unit under test, but call it whatever:
//always use named port convention when declaring (it is much easier to protect from bugs)
alu uut(.d0_in(d0_in), .d1_in(d1_in), .sel_in(sel_in),
.res_out(res_out), .gt_out(gt_out), .eq_out(eq_out));
//All simulations start with the the "initial block's top
// They then run forward in order like regular code.
//lines that are one after the other happen "instaneously together"
//Time passes using the # notation. (#10 is 10 nanoseconds)
// set the initial values of the module inputs
initial begin
d0_in = 0; //set d0_in to 0
d1_in = 0; //same for d1_in
sel_in = 0; //same for sel_in
// Extremely Important!
// Even though the system is combinatorial-only, make sure some simulation time runs before analyzing outputs
#10; //wait 10 ns
//now print something:
$display("\n---------\nStarting Simulation!");
d0_in = 12; //change values!
d1_in = 45;
// run through all operations and monitor outputs
$display("d1_in d0_in sel_in res_out eq_out gt_out");
for(integer i = 0; i < 8; i = i + 1) begin
sel_in = i; //set sel_in
#10; //wait for a bit of time (10 ns)
//then evaluate outputs:
$display("%8b %8b %3b %15b %b %b", d1_in, d0_in, sel_in, res_out, eq_out, gt_out);
end
$display("\n---------\nFinishing Simulation!");
$finish; //finish simulation.
end
endmodule // alu_tb
If you place the testbench file in your sim folder, from the root of your project folder, you can do:
iverilog -g2012 -o sim/alu.out sim/alu_tb.sv src/alu.sv
and then
vvp sim/alu.out
and outputs should appear. Use and expand this starting test case above to make sure your module is doing all the right operations when needed. Try a variety of input numbers! During Checkoff 2 you will be required to show your test case is testing your system working with at least:
d0=0andd1=0d0=100andd1=10d0=10andd1=100d0=42andd1=42d0=7andd1=42
When you feel confident that your module is working, run it in the checker below. Do not just write your code in the checker without testing locally.
If you've done a rigorous job testing your design in simulation, then we should be ready to deploy it onto hardware.
Make an instance of your alu module inside your current top_level module. Comment out the following three lines that are currently there, since you'll now be controlling those three sets of LEDs from your ALU. directly. :
assign led = sw;
assign led17_r = 1'b1;
assign led16_b = 1'b1;
In addition, so you don't get distracted by the seven segment LEDs for this stage, change the values of an to all be 1! This will turn off all the seven segment LEDs. Build, upload and make sure it works like the video below shows.
Show your testbench testing everything. Demonstrate your final stystem working to a staff member.