Skip to content

Lab 3: Music Player
Part A: Counters

Lab 3 will give you experience designing, implementing, testing, and prototyping combinational and sequential logic using the Verilog hardware description language. The lab will continue to leverage concepts from Topic 2: Combinational Logic, Topic 3: Boolean Algebra, and Topic 4: Combinational Building Blocks but will also leverage concepts from Topic 6: Sequential Logic, Topic 7: Finite-State Machines, and Topic 8: Sequential Building Blocks. More specifically, the lab will give students experience with: latches, flip-flops, and registers; Moore and Mealy FSMs; and counters. The lab will continue to reinforce three key abstraction principles: modularity, hierarchy, and regularity.

You will be implementing a music player that takes as input a song selection (via the switches) and a start song signal (via a push button). The music player will then play the chosen song by generating a square wave at appropriate note frequencies suitable for use with a piezoelectric buzzer. An idle signal is displayed using an LED so that the user knows when the player is ready to play a new song. The music player will make use of the adders and muxes from Lab 2. The song selection and the current note are both displayed using seven-segment displays from Lab 1. This lab also serves as a transition from lower-level gate-level (GL) modeling to higher-level register-transfer-level (RTL) modeling. Some of parts of your design will use explicit GL modeling, while other parts of your design will use RTL modeling. Students will have a chance to appreciate how RTL modeling can improve productivity but with less control over the final hardware implementation. The lab includes six parts:

  • Part A: Counters

    • Due 10/15 @ 11:59pm via GitHub
    • Students should work on Part A before, during, and after your assigned lab section during the week of 10/5
    • Pre-lab survey on Canvas is (roughly) due by end of lab section during the week of 10/5
  • Part B: Breadboard, FPGA, and Simulation

    • Due week of 10/5 during assigned lab section
    • Even though completed with a partner, every student must turn in their own paper check-off sheet in their lab section!
  • Part C: Music Player

    • Due 10/22 @ 11:59pm via GitHub
    • Plan to work on Part C after fall break and during the week of 10/19
  • Part D: FPGA Prototype v1

    • Due week of 10/19 during assigned lab section
    • This part will focus on prototyping the code developed in Part A
    • Even though completed with a partner, every student must turn in their own paper check-off sheet in their lab section!
  • Part E: FPGA Prototype v2

    • Due week of 10/26 during assigned lab section
    • This part will focus on prototyping the code developed in Part C
    • Even though completed with a partner, every student must turn in their own paper check-off sheet in their lab section!
  • Part F: Report

    • Due week of 10/26, three days after lab section @ 11:59pm via Canvas
    • Post-lab survey on Canvas is due at the same time as the report

All parts of Lab 3 must be done with a partner. You can confirm your partner on Canvas (Click on People, then Groups, then search for your name to find your lab group).

Both students must contribute to all parts!

It is not acceptable for one student to exclusively work on the code while the other student exclusively works on the report. It is not acceptable for one student to exclusively work on hardware design while the other student exclusively works on testing. Both students must contribute to all parts. Student understanding of Verilog design and testing will be assessed on the prelim exams, final exam, and Verilog coding exam. The instructors will also survey the Git commit log on GitHub to confirm that both students are contributing equally. If you are using pair programming, then both students must take turns using their own account so both students have representative Git commits. Students should create commits after finishing each step of the lab, so their contribution is clear in the Git commit log. If a student's contribution is limited, as represented by the Git commit log, then that student will receive a significant deduction to their lab score.

This handout assumes that you have read and understand the course tutorials and that you have attended the discussion sections. To get started, use VS Code to log into a specific ecelinux server, source the setup script, and use git pull to ensure you have any recent updates before working on your lab assignment.

 % source setup-ece2300.sh
 % cd ${HOME}/ece2300/groupXX
 % git pull
 % tree

where XX should be replaced with your group number. Go ahead and create a build directory, run configure to generate a Makefile, and run all of the tests.

% cd ${HOME}/ece2300/groupXX
% mkdir -p build
% cd build
% ../configure
% make check-lab3a

Your repo contains the following files which are part of the automated build system:

  • Makefile.in: Makefile for the build system
  • sprojs.mk: Makefile fragment listing subprojects
  • configure: Configure script for the build system
  • configure.ac: Used to generate the configure script
  • scripts: Scripts used by the build system

The following table shows all of the hardware modules you will be developing in Lab 3.

Lab 3 requires implementing and verifying 25 hardware modules. While this might seem like a daunting task, many of these hardware modules require less than five lines of Verilog code. For many of the test benches we provide students with a template so they need only enter the expected outputs. Test cases are reused across the GL and RTL versions of the same hardware module. Start from the D latch and systematically work your way down the table. Implement the GL and RTL versions at the same time. Never move on to the next row in the table until you have thoroughly tested the previous row.

GL modules must be implemented using explicit gate-level modeling. This means students are only allowed to use these Verilog constructs in their Verilog hardware designs:

  • wire (single bit and multiple bit)
  • not, and, nand, or, nor, xor, xnor
  • literals (e.g., 1'b0, 1'b1)
  • wire slicing (e.g., x[0], x[1:0])
  • concatentation (e.g., {x,y})
  • assign for connecting wires (e.g., assign x = y;);
  • assign for setting a wire to a constant value (e.g., assign x = 1'b0;)
  • module instantiation

RTL implementations can use the following Verilog constructs.

  • logic, assign
  • +, -, *
  • >>, <<
  • ==, !=, <, >, <=, >=
  • &&, ||, !
  • &, ~&, |, ~|, ^, ^~ (reduction operators)
  • ?: (ternary operator)
  • always_comb, always_ff @(posedge clk)
  • case, casez, default, endcase

RTL implementations are allowed to use task in very specific situations (e.g., when implementing the FSM output combinational logic). Note that some hardware modules have more specific restrictions; see the source comments for more details. Using unallowed Verilog constructs will result in significant penalties for code functionality and code quality. If you have any questions on what Verilog constructs can and cannot be used, please ask an instructor. There are no restrictions on Verilog constructs in test benches or interactive simulators.

1. Pre-Lab Survey

Take some time to meet with your partner to discuss the pre-lab survey which is on Canvas. The pre-lab survey includes questions on your learning outcomes, workload distribution, workload roadmap, communication, and collaboration. The survey is due (roughly) by the end of your assigned lab section the week of 10/5. Students with an early lab section on Monday might want to complete the pre-lab survey right after their lab section, while students with a late lab section on Wednesday should meet earlier in the week and complete the pre-lab survey then. A student will not receive a grade for the lab unless the pre-lab survey is completed.

2. Interface and Implementation Specification

You will be implementing and composing a variety of combinational and sequential hardware modules including adders, muxes, latches, flip-flops, registers, comparators, and counters; ultimately you will be composing these hardware modules to implement a note player. This section describe the required interface (i.e., the ports for the module and the module's functional behavior) before describing the required implementation (i.e., what goes inside the module) for each hardware module.

2.1. Latches and Flip-Flops

You will need to implement a GL D latch and three GL/RTL D flip-flops:

  • DLatch: D latch (level high, transparent when clock is one)
  • DFF: D flip-flop (positive edge triggered)
  • DFFR: D flip-flop with reset signal
  • DFFRE: D flip-flop with reset and enable signals

You should consult the lecture notes for the GL implementation of these latches and flip-flops. You just need to directly map the implementation from the lecture notes using explicit gate-level modeling.

Recall from the discussion sections that we will need to make use of an always_ff block for RTL modeling of sequential logic. Here is an example of how to implement a D flip-flop using an always_ff block:

1
2
3
always_ff @( posedge clk ) begin
  q <= d;
end

This always_ff block will execute on the positive edge (posedge) of the clock signal (clk). We are using a special non-blocking assignment (<=) which is meant to be used in always_ff blocks. The semantics of a non-block assignment are: (1) right before the rising edge, the right-hand side of every non-blocking assignment in the entire design is evaluated and the result is saved; (2) right after the rising edge, the saved values are assigned to the signal on the left-hand side. This enables us to model flip-flops.

Note that this the only form of an always_ff block you are allowed to use in this course. You are not allowed to use negedge and you are not allowed to trigger an always_ff with any other signals (e.g., in this course, you are not allowed to include reset (rst) in the sensitivity list for an always_ff).

You are allowed to uase an in/else conditional in an always_ff block. For example, you can use an if/else conditional operation within an always_ff block to model a D flip-flop with a (synchronous) reset signal:

always_ff @( posedge clk ) begin

  if ( rst )
    q <= 1'b0;
  else if ( !rst )
    q <= d;
  else
    q <= 'x;

end

As mentioned in lecture, the final else is needed to explicitly propgate X values to avoid X-optimism. If the rst signal is X we need to make sure that the output q is also X.

Note that in this course we will only allow using an always_ff block in very specific modules. Namely within the implementation of single-bit flip-flops (i.e., DFF_RTL, DFFR_RTL, DFFRE_RTL) and within the implementation of the multi-bit register (e.g., Register_16b_RTL). Later you will also use an always_ff block when implementing a memory and a shift register. Students are not allowed to use an always_ff anywhere else in their hardware designs. If a hardware design needs sequential logic, then the design should instantiate one of these single-bit flip-flops or multi-bit registers. This restriction is critical to enabling new Verilog designers to know exactly what hardware is being implemented. Arbitrarily using always_ff blocks can quickly lead to incredibly complicated hardware models with no hope of understanding what hardware we are actually implementing.

Once you have finished implementing all of the latches and flip-flops, take a minute to appreciate the relationship between the GL and RTL implementations. The RTL implementations are just a different way of modeling the hardware within the GL implementation. The FPGA tools will synthesize the RTL implementation into hardware very similar to what is in the GL implementation.

2.2. Register

As discussed in lecture, we can create a GL multi-bit register by simply instantiating a number of GL flip-flops in parallel. The data input (d) and data output (q) of each flip-flop should be connected to their corresponding bit of the register's multi-bit input and output ports. The clock, reset, and enable signals should be connected to each flip-flop.

For RTL multi-bit registers there is no need to instantiate a number of RTL flip-flops. We can instead model a multi-bit register using a single always_ff block. A single non-blocking assignment can be used to assign the multi-bit input to the multi-bit output on the rising edge of the clock. Since the register includes a reset and enable signal, you will also need to include an if/else conditional.

2.3. Equality Comparator

We need a GL 16-bit equality comparator to implement the counter. You will need many XNOR or XOR gates depending on how you want to immplement your comparator. You are allowed to use logic gates with many inputs if you like. So for example, it is fine to use a 16-input AND gate or a 16-input OR gate.

For the RTL 16-bit equality comparator you can simply use a single assign statement.

1.2. Multiplexors

We will need two RTL multiplexors for the counter and eventually the music player: a 2-to-1 16-bit RTL multiplexor and a 8-to-1 1-bit multiplexor. You should implement the 2-to-1 16-bit RTL multiplexor with a single assign and ternary statement, while you should implement the 8-to-1 1-bit multiplexor with a single always_comb and case statement.

2.5. Counter

We will be implementing a relatively general-purpose count-up 16-bit counter that can support starting and finishing from arbitrary values, incrementing by an arbitrary value, and pausing the counter. We will be implementing the counter in three ways:

  • Structural GL implementation mostly through instantiating GL modules
  • Structural RTL implementation mostly through instantiating RTL modules
  • Flat RTL implementation mostly through assign statements

We will unit test the counter and then reuse it in several places in both Lab 3 and Lab 4. The counter has the following interface:

When load is high (regardless of the value of en), the counter should register the start and finish values at the end of the cycle. If start or finish change while load is low, then these changes should be ignored.

When enable (en) is high, the counter increments from the registered start value up to the registered finish value in incr steps, checking for equality on each cycle. Once the finish value is reached the counter holds at the finish value. When enable is low, the counter holds its current value and should not increment.

The count output should always reflect the current count (i.e., it should not reflect the next count).

The done signal should always reflect whether or not the current count and the registered finish values are equal. If they are equal then the done signal should be high. If they are not equal then the done signal should be low. If start equals finish, the counter immediately asserts done after loading, since the initial count matches finish. If load is one while the counter is not done, then the counter should simply register the new start and finish values at the end of the cycle and start counting from the new start value on the next cycle.

The counter assumes the following about the inputs; behavior is undefined if any of these conditions is not satisfied.

  • incr must can only change when the counter is in reset
  • start, finish, and incr are 16-bit unsigned binary values
  • start must be less than or equal to finish
  • difference between finish and start must be divisible by incr

Here is a trace of the expected output when counting from 2 to 4 twice.

cyc  en ld start  incr   finish   count done
  0: 1  1 (0002, +0001 -> 0004) > 0000  1
  1: 1  0 (0002, +0001 -> 0004) > 0002  0
  2: 0  0 (0002, +0001 -> 0004) > 0003  0    # counter disabled
  3: 0  0 (0002, +0001 -> 0004) > 0003  0    # counter disabled
  4: 1  0 (0002, +0001 -> 0004) > 0003  0
  5: 1  0 (0002, +0001 -> 0004) > 0004  1
  6: 1  0 (0002, +0001 -> 0004) > 0004  1
  7: 1  0 (0002, +0001 -> 0004) > 0004  1
  8: 1  1 (0002, +0001 -> 0004) > 0004  1
  9: 1  0 (0002, +0001 -> 0004) > 0002  0
 10: 0  0 (0002, +0001 -> 0004) > 0003  0    # counter disabled
 11: 0  0 (0002, +0001 -> 0004) > 0003  0    # counter disabled
 12: 1  0 (0002, +0001 -> 0004) > 0003  0
 13: 1  0 (0002, +0001 -> 0004) > 0004  1

Pay careful attention to the exact cycle the output count is set to four and the exact cycle the done signal is set to one. Your counter must produce the exact same cycle-level behavior.

You are responsible for designing your own GL 16-bit counter that meets this specification. Your GL implementation must only use GL modules or explicit gate-level modeling. Do not put any logic on the reset signal. The reset signal should be directly attached to the reset input port of the register without any logic. Do not put any logic on the clock. The clock signal should be directly attached to the clock input port of the register without any logic. You must draw a block diagram of your final counter. You will need this diagram for later parts of the lab.

You are also responsible for designing two RTL 16-bit counters that meet this specification: a structural RTL implementation and a flat RTL implementation.

The structural RTL implementation should only instantiate RTL modules with a few logic gates. Do not put any logic on the reset signal. The reset signal should be directly attached to the reset input port of the register without any logic. Do not put any logic on the clock. The structural implementation should use the exact same block diagram of the counter you drew for the GL implementation. This means creating this implementation is straight-forward. You simply need to start with your GL implementation and change every _GL suffix to _RTL. You might also need to change the name of the adder you are using.

The flat RTL implementation must explicitly instantiate an RTL register for the sequential logic. You cannot directly use an always_ff block in your RTL counter. You must explicitly instantiate an RTL register. Do not put any logic on the reset signal. The reset signal should be directly attached to the reset input port of the register without any logic. Do not put any logic on the clock. The flat RTL implementation should use a series of assign statements for the combinational logic. The flat RTL implementation should not instantiate any combinational RTL modules. The structural implementation should use the exact same block diagram of the counter you drew for the GL implementation. This means creating this implementation is straight-forward. You simply need to use an assign statement for each block in your block-level diagram:

  • use assign and + to implement an adder
  • use assign and the ternary operator (?,:) to implement a mux
  • use assign and == to implement an equality comparator
  • use assign and Boolean operators to implement logic gates

All three implementations must implement the same block diagram. Students are not allowed to have extra muxes or adders or registers in a flat RTL implementation which are not also in the structural RTL and structural GL implementations. All three implementations must implement the same block diagram.

Use an incremental design approach!

This is a relatively complex hardware module that students must implement from scratch. We strongly discourage using a big bang design approach. Do not just start writing code, try to implement the entire specification in one shot, and then see if you can get it to work. This approach can easily take 3+ hours. Instead, use an incremental design approach. Start by implementing a simple, incomplete version of the counter, get that passing simple tests, then add one piece of functionality, get that passing more complicated tests, and so on.

To help students learn an incremental design approach we recommend using the following five incremental steps. Draw a block diagram for each step before writing any code! We have included five basic test cases that correspond to these five steps.

  • Step 1: Implement a counter that is reset to zero and then simply counts up forever. This incomplete counter can mark the start, finish, load, and en inputs to the counter as unused and leave the done output of the counter undriven. This incomplete design should pass the basic_v1 test case. Here is the corresponding block diagram.

  • Step 2: Extend the counter from step 1 to support loading in a new start value but ignore the finish and en inputs to the counter. Leave the done output of the counter undriven. This incomplete design should pass the basic_v2 test case. Remember to draw a block diagram before writing any code.

  • Step 3: Extend the counter from step 2 to support loading in a new finish value but ignore the en input to the counter. The incomplete counter should set the done output correctly, but does not need to stop when done. This incomplete design should pass the basic_v3 test case. Remember to draw a block diagram before writing any code.

  • Step 4: Extend the counter from step 3 to support stopping when done. Continue to ignore the en input. This incomplete design should enable counting, stopping when done, and then counting again. This incomplete design should pass the basic_v4 test case. Remember to draw a block diagram before writing any code.

  • Step 5: Extend the counter from step 4 to support the en input to the counter. The counter should hold the current count when en is zero. This is the complete counter and should pass the basic_v5 test case. Remember to draw a block diagram before writing any code.

Follow these five steps for the GL implementation. Then you may be able to just translate your GL implementation into the structural and flat RTL implementations, or you might want to follow the exact same five steps for the RTL implementations. Remember, all three implementations should be implementing the same block diagram. If you follow this process you will always be making progress; the entire experience will be less frustrating and take less time.

You really do need a GL counter block diagram!

Once you have your GL counter working you really do need to make sure you have a clean block diagram which shows all of the registers, muxes, adders, etc and how they are connected. You will need this for the RTL implementations and to show this to the TAs in Part D. You will annotate this diagram with the actual critical path and component delays as analyzed by Quartus. If you do not draw this block diagram now, you will need to draw it during Part D wasting precious time!

3. Testing Strategy

You are responsible for developing an effective testing strategy to ensure all implementations are correct. Writing tests is one of the most important and challenging aspects of designing hardware. Hardware engineers often spend far more time implementing tests than they do implementing the actual hardware design.

3.1. Explicit vs Implicit Clock/Reset Testing

In this lab, some of our tests will use explicit clock/reset testing, others will use implicit clock/reset testing, and some will use a hybrid approach.

With explicit clock/reset testing the clock and reset signals are explicitly set in the check task as part of the test case. For example, the test cases for DFFRE look like this:

task test_case_1_basic();
  t.test_case_begin( "test_case_1_basic" );

  //    clk rs en d  q
  check( 0, 1, 1, 0, 1'b? ); // output undefined
  check( 1, 1, 1, 0, 0 );
  check( 0, 0, 1, 0, 0 );
  check( 1, 0, 1, 0, 0 );
  check( 0, 0, 1, 0, 0 );

  t.test_case_end();
endtask

Notice that the clock and reset signals are part of the check task. We are explicitly toggling the clock and controlling the reset signal. This kind of testing gives you more control but can also be more tedious.

With implicit clock and hybrid reset testing the clock signal is handled by the ECE 2300 unit testing framework and is not set in the check task as part of the test case. The reset signal is handled both implicitly by the ECE 2300 unit testing framework and also explicitly in the check tasks. For example, the test cases for the Register_16b look like this:

task test_case_1_basic();
  t.test_case_begin( "test_case_1_basic" );

  //    rst en d                        q
  check( 0, 1, 16'b0000_0000_0000_0000, 16'b0000_0000_0000_0000 );
  check( 0, 1, 16'b0000_0000_0000_0001, 16'b0000_0000_0000_0000 );
  check( 0, 1, 16'b0000_0000_0000_0000, 16'b0000_0000_0000_0001 );
  check( 0, 1, 16'b0000_0000_0000_0010, 16'b0000_0000_0000_0000 );
  check( 0, 1, 16'b0000_0000_0000_0000, 16'b0000_0000_0000_0010 );

  t.test_case_end();
endtask

And the corresponding test bench in Register_16b_GL-test.v looks like this:

logic clk;
logic rst_implict;

TestUtilsClkRst t
(
  .clk (clk),
  .rst (rst_implicit)
);

logic        rst;
logic        en;
logic [15:0] d;
logic [15:0] q;

Register_16b_GL register
(
  .clk (clk),
  .rst (rst),
  .en  (en),
  .d   (d),
  .q   (q)
);

logic rst_explicit;
assign rst = rst_implicit | rst_explicit;

So in this case the clock is completely handled by the ECE 2300 unit testing framework. The reset signal is controlled by both the ECE 2300 unit testing framework and the check task. This means the design under test will be reset implicitly before each test case, but we can also write directed test cases that explicitly reset the design under test in the middle of the test case.

Finally, in Part C we will be using implicit clock/reset testing in which we let the ECE 2300 unit testing framework handle both the clock and reset signals for us.

3.2. Basic Testing

We will be using the same lightweight testing framework from the previous labs. For each hardware module, we provide a test bench for you to use along with one basic test case. You can run the basic tests for all hardware modules using the generated Makefile.

% cd ${HOME}/ece2300/groupXX/build
% make check-lab3a

You can also build and run a single test simulator.

% cd ${HOME}/ece2300/groupXX/build
% make DLatch_GL-test
% ./DLatch_GL-test

You can specify which specific test case to run on the command line and also dump waveforms that can be viewed using Surfer.

% cd ${HOME}/ece2300/groupXX/build
% make DLatch_GL-test
% ./DLatch_GL-test +test-case=1
% ./DLatch_GL-test +test-case=1 +dump-vcd=waves.vcd

3.3. Exhaustive Testing

The following test benches are for hardware modules with a limited number of inputs, and thus you can (and should) use exhaustive testing:

  • DLatch_GL-test.v
  • DFF_GL-test.v, DFF_RTL-test.v

We provide test case templates for these test benches so all you need to do is fill in the expected output. You do not need to add any more test cases. Notice that DFF_GL and DFF_RTL have the exact same interface and thus will use the exact same test cases. We have refactored the test cases for these hardware modules into DFF-test-cases.v and then included these test cases in DFF_GL-test.v and DFF_RTL-test.v. This way we only need to write the test cases once.

3.4. Directed Testing

The remaining hardware modules all have many more inputs and thus would required hundreds or even thousands of checks to implement exhaustive testing. So for the remaining hardware modules you can use directed testing to check for specific corner cases.

We provide test case templates for the following test benches so all you need to do is fill in the expected output. You do not need to add any more test cases.

  • DFFR_GL-test.v, DFFR_RTL-test.v
  • DFFRE_GL-test.v, DFFRE_RTL-test.v
  • Register_16b_GL-test.v, Register_16b_RTL-test.v

For some of these these we have again refactored the test cases for these hardware modules into a separate -test-cases.v file. You must write your own directed test cases for the following test benches:

  • EqComparator_16b_GL-test.v, EqComparator_16b_RTL-test.v
  • Mux2_16b_RTL-test.v, Mux8_1b_RTL-test.v
  • Counter_16b_GL-test.v, Counter_16b_struct_RTL-test.v, Counter_16b_flat_RTL-test.v

You can copy the tests you wrote for Mux2_16b_GL-test.v in lab 2 if you like. Remember, each directed test case should have an informative name and focus on testing a very specific kind of functionality and they should contain 2-10 checks. It is rare to have a single directed test case task; you will almost certainly want multiple tasks with each task focused on a different directed test. Be sure to add your tests cases to the list in the initial block and to check the output of the test simulator to confirm that your directed test cases are running and testing what you expect them to. Consider purposefully inserting a bug in your designs to see if your directed test cases will catch the bug.

3.5. Random Testing

Directed testing is useful for testing the known unknowns, but what about the unknown unknowns? How should we test for corner cases we have not even thought of yet? Random testing can help increase our testing coverage and increase our confident that our hardware design is functionally correct. You should implement one random test case for each of the following hardware modules:

  • EqComparator_16b_GL-test.v, EqComparator_16b_RTL-test.v
  • Mux2_16b_RTL-test.v, Mux8_1b_RTL-test.v
  • Counter_16b_GL-test.v, Counter_16b_struct_RTL-test.v, Counter_16b_flat_RTL-test.v

Random test cases should include a for loop. Each iteration should: (1) generate random input values; (2) use Verilog test code to programmatically determine the correct output values; and (3) use the check task to ensure the design-under-test produces the correct outputs give the corresponding random inputs.

3.6. X-Propagation Testing

You must also include one X-propagation test case for all of the modules in this part. Some of these test benches already include a template that you just need to fill in. If nothing else, check that if all of the inputs are X then all of the outputs are X.

3.7. Interactive Simulators

We have provided you an interactive simulator which will emulate the FPGA prototype you will be demoing in the lab. After finishing implementing and thoroughly testing your RTL counter, you can build and run the simulator for the counter like this:

% cd ${HOME}/ece2300/groupXX/build
% make counter-sim
% ./counter-sim +start-switches=0001 +finish-switches=00101

The switches are connected to the counter input. The counter simulator will display the input and the output of the count-down counter by showing what the four seven segment displays would look like on the FPGA prototype. You can press enter to emulate the clock toggling.

4. Lab Code Submission

To submit your code you simply push your code to GitHub. You can push your code as many times as you like before the deadline. Students are responsible for going to the GitHub website for your repository, browsing the source code, and confirming the code on GitHub is the code they want to submit is on GitHub Be sure to verify your code is passing your tests both on ecelinux and on GitHub Actions. Your design code will be assessed both in terms of code quality, verification quality, and functionality.

5.1. Code Quality

Your code quality score will be based on how well you follow the course coding conventions posted here:

Code quality for Part A will be assessed after the Part C deadline.

5.2. Verification Quality

Verification quality is based on how well your testing enables making a compelling case for correctness. You will need to write compelling directed test cases, use reasonable randomg testing, and include a simple X-propgation test case. Use comments appropriately to describe your test cases. Verification quality for Part A will be assessed after the Part A deadline.

5.3. Functionality

Your functionality score will be determined by running your code against a series of tests developed by the instructors to test its correctness. Note that we will be using the automated build system to test your final code submission as shown below.

% mkdir -p ${HOME}/ece2300
% cd ${HOME}/ece2300
% git clone git@github.com:cornell-ece2300/groupXX
% cd groupXX

% mkdir -p build
% cd build
% ../configure
% make check-lab3a