• SCOPE
    • LOCAL NODE.JS EXPRESS SERVER FOR DEVELOPING THIS SITE BEFORE PUBLISHING TO NEOCITIES
    • SERVES STATIC HTML, CSS, JS, IMAGES, AND MUSIC WITH BLOCKED ACCESS TO SERVER FILES
  • CODE — server.js
  • // Author: Mya Anderson myaxd.neocities.org
    // Author: Mya Anderson myaxd.neocities.org
    // Feel free to use this code for your own personal website development! 
    // Credit me if posting anywhere please!
    // Node.js express server for local development of website. I use this to develop my site befor release onto Neocities
    
    // 1. To run: make sure server.js is in the same folder as static - your folder hierarchy has to look like this:
    /*
    project-root/
    │
    ├── server.js
    ├── package.json
    ├── package-lock.json
    ├── node_modules/
    │
    └── static/
        │
        ├── html/
        │   ├── index.html
        │   └── 404.html
        │
        ├── css/
        │   └── indexstyle.css
        │
        ├── js/
        │   └── app.js
        │
        ├── images/
        │   └── background.png
        │
        ├── music/
        │   └── song.mp3
        │
        └── pdfs/
            └── document.pdf
    */
    // 2. Install Node.js
    // 3. in a cmd prompt: navigate to your server dir
    //    3.a Run the command: npm install express
    // 4. Locate package.json file
    //    4.a Change the type to "module" For example it will look like this: ["type": "module"]
    // 5. Finally, run the server from cmd: node server.js
    
    import express from "express";
    import { fileURLToPath } from "url";
    import path from "path";
    
    const __filename = fileURLToPath(import.meta.url);
    const __dirname = path.dirname(__filename);
    
    const port = 6767;
    const app = express();
    
    const htmlDir = path.join(__dirname, "static", "html");
    const cssDir = path.join(__dirname, "static", "css");
    const jsDir = path.join(__dirname, "static", "js");
    const imgDir = path.join(__dirname, "static", "images");
    const musicDir = path.join(__dirname, "static", "music");
    const pdfDir = path.join(__dirname, "static", "pdfs");
    const notFoundPage = path.join(htmlDir, "not_found.html");
    
    const blocked = new Set(["server.js", "package.json", "package-lock.json"]);
    
    app.use((req, res, next) => {
      if (blocked.has(path.basename(req.path)) || req.path.startsWith("/node_modules")) {
        return res.status(404).sendFile(notFoundPage);
      }
      next();
    });
    
    // HTML pages use flat paths like /index.html and /
    app.use(express.static(htmlDir, { index: "index.html" }));
    
    // CSS and JS are referenced at the site root (e.g. /indexstyle.css, /scroll.js)
    app.use(express.static(cssDir));
    app.use(express.static(jsDir));
    app.use(express.static(musicDir));
    
    // Images are referenced as /images/*
    app.use("/images", express.static(imgDir));
    
    // PDFs are referenced as /pdfs/*
    app.use("/pdfs", express.static(pdfDir, {
      setHeaders(res, filePath) {
        if (path.extname(filePath).toLowerCase() === ".pdf") {
          res.setHeader("Content-Type", "application/pdf");
          res.setHeader("Content-Disposition", "inline");
        }
      }
    }));
    
    app.use((req, res) => {
      res.status(404).sendFile(notFoundPage);
    });
    
    app.listen(port, () => {
      console.log(`Server running at http://localhost:${port}/`);
    });
                  
    
  • HOW TO:
    • 1. Install Node.js

      2. in a cmd prompt: navigate to your server directory

      3. Run the command: npm install express

      4. Locate package.json file and change the type to "module" For example it will look like this: ["type": "module"]

      5. Finally, run the server from cmd: node server.js

DIFFICULTY:

SKILL:

INTEREST

  • SCOPE
    • HAMMING CODE CALCULATOR AND VERIFIER FOR 8 BIT DATA WORDS USING VERILOG AND VIVADO ON A BOOLEAN BOARD FPGA
    • A concern for designers of digital systems is the detection and correction of transient errors — errors that occur for a short period of time and are not detected by the system. The Hamming code is a type of error-correcting code that can be used to detect and correct single bit errors, which is especially important in critical systems.
  • THEORY
    • In the photo to the left, each number that contains only a 1 represents a parity bit. Each other number is a data bit. Each parity bit is the XOR of the data bits in the positions it checks.
    • Each data bit is checked by two parity bits — or it is an input of at least two parity bits. Furthermore, because binary counting is a form of enumeration, no two data bits are inputs of the same set of parity bits. Because the two sets are not the same, at least one parity bit must be exclusive to one of the two data bits that are flipped in a double-bit error and this same bit will invert as if there was only a single-bit error. The single-bit error correction capability of Hamming codes are a byproduct of this same property: every data bit has an identifying set of parity bits that will flip if the data bit flips. Invert that data bit, and the parity error will disappear.
    • Hamming codes can be used to detect and correct single-bit errors, and they can be used to detect double-bit errors, but not always — as we see in the following example.
    • Hamming codes must be Hamming (n,k) codes, where n is the total number of bits in the codeword and k is the number of data bits. r is the number of parity bits. The relationship between n, k, and r must be satisfied for a Hamming code to be valid: 2^r >= k + r + 1.
  • CODE — top.v
  • // Author: Mya Anderson myaxd.neocities.org
    // Top module for the Hamming code calculator and verifier.
    // Calculates parity bits for the original and inverted data,
    // and displays results on LEDs, 7-segment display, buttons, and switches.
    
    `timescale 1ns / 1ps
    
    module top (
      output [15:0] o16lLED,
      output [ 7:0] o8lSSeg0Cathode,
      output [ 3:0] o4lSSeg0Anode,
      input         isClk,
      input  [ 3:0] i4lButton,
      input  [15:0] i16lSwitch
    );
    
      wire [7:0] original_data;
      wire [7:0] inverted_data;
      wire [3:0] parityOld;
      wire [3:0] parityInv;
    
      assign original_data = i16lSwitch[7:0];
      assign inverted_data = i16lSwitch[15:8];
    
      assign o16lLED[7:0] = original_data ^ inverted_data;
    
      hamming_calculator calcOriginalP(original_data, parityOld);
      hamming_calculator calcInvertedP(original_data ^ inverted_data, parityInv);
    
      assign o16lLED[11:8]  = parityOld;
      assign o16lLED[15:12] = parityInv;
    
    endmodule
    
  • CODE — hamming_calculator.v
  • // Author: Mya Anderson myaxd.neocities.org
    // Calculates the parity bits for an 8-bit data word.
    
    `timescale 1ns / 1ps
    
    module hamming_calculator (
      input  [7:0] i8lData,
      output [3:0] o4lParity
    );
    
      assign o4lParity[0] = i8lData[0] ^ i8lData[1] ^ i8lData[3] ^ i8lData[4] ^ i8lData[6];
      assign o4lParity[1] = i8lData[0] ^ i8lData[2] ^ i8lData[3] ^ i8lData[5] ^ i8lData[6];
      assign o4lParity[2] = i8lData[1] ^ i8lData[2] ^ i8lData[3] ^ i8lData[7];
      assign o4lParity[3] = i8lData[4] ^ i8lData[5] ^ i8lData[6] ^ i8lData[7];
    
    endmodule
    
  • CODE — hamming_verifier.v
  • // Author: Mya Anderson myaxd.neocities.org
    // Verifies parity bits for the original data.
    
    `timescale 1ns / 1ps
    
    module hamming_verifier (
      input  [7:0] i8lData,
      input  [3:0] i4lParity,
      output       osDataError
    );
    
      wire [3:0] c;
    
      hamming_calculator calc(i8lData, c);
    
      assign osDataError = |(c ^ i4lParity);
    
    endmodule
    
  • CODE — top_tb.v
  • 
    // Author: Mya Anderson myaxd.neocities.org
    
    `timescale 1ns / 1ps
    
    module top_tb();
    
      reg         isClk;
      reg  [ 3:0] i4lButton;
      reg  [15:0] i16lSwitch;
      wire [15:0] o16lLED;
      wire [ 7:0] o8lSSeg0Cathode;
      wire [ 3:0] o4lSSeg0Anode;
    
      top fpga (
        .o16lLED(o16lLED),
        .o8lSSeg0Cathode(o8lSSeg0Cathode),
        .o4lSSeg0Anode(o4lSSeg0Anode),
        .isClk(isClk),
        .i4lButton(i4lButton),
        .i16lSwitch(i16lSwitch)
      );
    
      initial begin
        isClk = 0;
        forever #5 isClk = ~isClk;  // 100 MHz clock
      end
    
      initial begin
        i4lButton  = 4'b0000;
        i16lSwitch = 16'b10100011_00000001;
        #10;
    
        $display("original         = %b", i16lSwitch[7:0]);
        $display("inverted         = %b", i16lSwitch[15:8]);
        $display("corrupted        = %b", o16lLED[7:0]);
        $display("original parity  = %b", o16lLED[11:8]);
        $display("inverted parity  = %b", o16lLED[15:12]);
    
        if (o16lLED[11:8] == o16lLED[15:12]) begin
          $display("Parity matches - ERROR NOT DETECTED (4-bit corruption passed)");
        end else begin
          $display("Parity mismatch - Error detected");
        end
    
        #10;
        $finish;
      end
    
    endmodule
    
  • CONCLUSION
    • Results are in the PDF below.

DIFFICULTY:

SKILL:

INTEREST

  • SCOPE
    • Create a clock divider to generate a 1Hz clock signal, which allows us to generate and display a random number on a 7-segment display using a LFSR.
    • Implement a Galois LFSR based PRNG.
    • Use multiplexing to program a 7-segment display to display the random number in hexadecimal.
    • Testbench to verify the functionality of the LFSR and clock divider.
  • THEORY
    • A Linear Feedback Shift Register (LFSR) is a type of shift register that is used to generate a sequence of random numbers.
    • A Galois LFSR is a type of LFSR that is used to generate a sequence of random numbers. An 8-bit Galois LFSR has 4 taps, which are the positions of the bits that are XORed together to produce the next bit. below we can see an LFSR with taps at q[7], q[5], q[4], and q[3]
    • The "seed" is the initial value of the lfsr, which is used to generate the sequence of random numbers. This seed in input by the user using the switches of the Boolean Board. When the user resets the LFSR using a isRst button, the LFSR resets to the original seed.
    • The seed is ideally obtained from some source of entropy, such as the time of day, the temperature, or the position of the stars - to make the generated sequence not predictable, but for testing, we use switches.
  • CODE — clock_divider.v
    • First, the clock divider is a module that divides the clock signal by an inputted divisor value. This is used to generate a 1Hz clock signal.
    • You may have noticed that the timescale is 1 ns / 1ns. This directive informs the simulator to count delay times in 1ns increments, and record the simulation state for each unit given second (once per ns). The smaller time intervals allows for better precision for faster circuits, but since clock divider is a slow circuit, it will take an inordinate amount of time to finish the simulation.
    `timescale 1 ns / 1ns
    `timescale 1 ns / 1ns
    
    module clock_divider (
        output reg osClk1Hz,
        input  isClk100MHz,
        input wire reset,
        input wire [31:0] divisor
    );
    
    
    reg [26:0] counts = 0; 
    
    always @(posedge isClk100MHz) begin
        if (reset) begin
            counts <= 0;
            osClk1Hz <= 0;
        end else if (counts == ((divisor / 2) - 1)) begin
            osClk1Hz <= ~osClk1Hz;
            counts <= 0;
        end else begin
            counts <= counts + 1;
        end
    end
    
    endmodule
    
  • CODE — clock_divider_tb.vg
  • 
    `timescale 1ns / 1ps
    
    module clock_divider_tb;
    
        reg isClk100MHz = 0; 
        reg reset;
        reg [1:0] counter;
        wire osClk1Hz;
    
        clock_divider clkdiv (
            .osClk1Hz(osClk1Hz),
            .isClk100MHz(isClk100MHz),
            .reset(reset)
        ); 
        
        always begin 
            #5 isClk100MHz = ~isClk100MHz;
        end 
    
        // Stimulus
        initial begin
            counter = 0; 
            reset = 1;
            #100;
            reset = 0;
        end
    
        always @(posedge osClk1Hz) begin
            counter <= counter + 1;
    
            if (counter == 3) begin 
                $finish; 
            end 
        end
    
    endmodule
                
  • CODE — decoder.vg
    • The clock divider also allows us to slow down the clock signal for a 7 segment display. At 100MHz, we wont be able to see the changes on the LED display.
    • Below we can see how the 7-segment display is wired to an FPGA, where the cathodes select which segment to light, and the anodes select which digit to display.
    • The module "decoder.vg" is a module which decodes the 8-bit binary sequence into a hex character 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, b, C, d, E, F,
    
    `timescale 1ns / 1ps
    
    module decoder (
        output reg [7:0] o8lSSeg,
        input  [3:0] i4lHexDigit
    );
    
    always @(*) begin
            case (i4lHexDigit)
                4'h0: o8lSSeg = 8'b11000000; // 0
                4'h1: o8lSSeg = 8'b11111001; // 1
                4'h2: o8lSSeg = 8'b10100100; // 2
                4'h3: o8lSSeg = 8'b10110000; // 3
                4'h4: o8lSSeg = 8'b10011001; // 4
                4'h5: o8lSSeg = 8'b10010010; // 5
                4'h6: o8lSSeg = 8'b10000010; // 6
                4'h7: o8lSSeg = 8'b11111000; // 7
                4'h8: o8lSSeg = 8'b10000000; // 8
                4'h9: o8lSSeg = 8'b10011000; // 9
                4'hA: o8lSSeg = 8'b10001000; // A
                4'hB: o8lSSeg = 8'b10000011; // b
                4'hC: o8lSSeg = 8'b11000110; // C
                4'hD: o8lSSeg = 8'b10100001; // d
                4'hE: o8lSSeg = 8'b10000110; // E
                4'hF: o8lSSeg = 8'b10001110; // F
                default: o8lSSeg = 8'b1111_1111;
            endcase
        end
    endmodule
                
  • CODE — lfsr.vg
  • 
    `timescale 1ns / 1ps
    
    module lfsr (
        output reg [15:0] b16lPRN,
        output [15:0] o16lLED,
        input [15:0] i16lSeed,
        input isClk,
        input isRst
    );
    
    always @(posedge isClk) begin
        if (isRst == 1) begin
            b16lPRN <= i16lSeed;
        end else begin
            b16lPRN[0]  <= b16lPRN[1];
            b16lPRN[1]  <= b16lPRN[2];
            b16lPRN[2]  <= b16lPRN[3];
            b16lPRN[3]  <= b16lPRN[4];
            b16lPRN[4]  <= b16lPRN[5];
            b16lPRN[5]  <= b16lPRN[6];
            b16lPRN[6]  <= b16lPRN[7];
            b16lPRN[7]  <= b16lPRN[8];
            b16lPRN[8]  <= b16lPRN[9];
            b16lPRN[9]  <= b16lPRN[10];
            b16lPRN[10] <= b16lPRN[11] ^ b16lPRN[0];
            b16lPRN[11] <= b16lPRN[12];
            b16lPRN[12] <= b16lPRN[13] ^ b16lPRN[0];
            b16lPRN[13] <= b16lPRN[14] ^ b16lPRN[0];
            b16lPRN[14] <= b16lPRN[15];
            b16lPRN[15] <= b16lPRN[0];
        end
    end
            assign o16lLED = b16lPRN;
    
    endmodule
                
  • CODE — top.vg
    • Lastly, the top module wraps everything together and instantiates the clock divider, decoder, and LFSR modules.
    
    `timescale 1ns / 1ps
    
    module top (
        output [15:0] o16lLED,
        output wire [7:0] o8lSSeg0Cathode,
        output reg [3:0] o4lSSeg0Anode,
        input         isClk,
        input  [3:0]  i4lButton,
        input  [15:0] i16lSwitch
    );
    
        // Clock signals
        wire osClk1Hz;       
        wire osClk7seg;      
    
        wire [15:0] b16lPRN;
        reg [3:0] current_digit;
        reg [1:0] digit_select = 0;
    
        
        lfsr lfsr (
            .b16lPRN(b16lPRN),
            .i16lSeed(i16lSwitch),
            .isClk(osClk1Hz),
            .isRst(i4lButton[0])
        );
        assign o16lLED = b16lPRN;
        
        clock_divider clk_div1Hz (
            .osClk1Hz(osClk1Hz),
            .isClk100MHz(isClk),
            .divisor(100_000_000)
        );
    
        clock_divider clk_div7seg (
            .osClk1Hz(osClk7seg),
            .isClk100MHz(isClk),
            .divisor(260_000)
        );
    
        decoder seg_decoder (
            .o8lSSeg(o8lSSeg0Cathode),
            .i4lHexDigit(current_digit)
        );
        initial begin 
            o4lSSeg0Anode = 4'b1111;  // Disable all
        end
    
        // Multiplexing logic for 7-segment display
        always @(posedge osClk7seg) begin
            case (digit_select)
                2'b00: begin
                    current_digit <= b16lPRN[3:0];
                    o4lSSeg0Anode <= 4'b1110;  // Enable digit 0 (rightmost)
                end
                2'b01: begin
                    current_digit <= b16lPRN[7:4];
                    o4lSSeg0Anode <= 4'b1101;  // Enable digit 1
                end
                2'b10: begin
                    current_digit <= b16lPRN[11:8];
                    o4lSSeg0Anode <= 4'b1011;  // Enable digit 2
                end
                2'b11: begin
                    current_digit <= b16lPRN[15:12];
                    o4lSSeg0Anode <= 4'b0111;  // Enable digit 3 (leftmost)
                end
                default: 
                    o4lSSeg0Anode <= 4'b1111;  // Disable all
            endcase
            digit_select <= digit_select + 1;
            if (digit_select > 3) begin
                digit_select = 0;
            end
        end
    
    endmodule
                
  • CODE — top_tb.v
  • 
    `timescale 1ns / 1ns
    
    module top_tb ();
    
        //clock divider
        reg isClk100MHz;
        wire osClk1Hz;
        reg [2:0] stop = 0;
        reg reset; 
        
        //lfsr
        wire [15:0] b16lPRN;
        reg [15:0] i16lSeed = 8'b0000_0000_1100_0000;
        reg isRst = 1;
    
        clock_divider clock_divider (
            .osClk1Hz(osClk1Hz),
            .isClk100MHz(isClk100MHz),
            .reset(reset),
            .divisor(100_000_000)
        );
        
        lfsr lfsr (
            .b16lPRN(b16lPRN),
            .i16lSeed(i16lSeed),
            .isClk(osClk1Hz),
            .isRst(isRst)
        );
    
        always begin
        #5 isClk100MHz=~isClk100MHz;
        end
        
        initial begin
            isClk100MHz = 0;
            reset = 1;
            #10;
            reset = 0;
        end
    
        initial begin
            #550_000_000 isRst = 0;
        end
    
        always @(posedge osClk1Hz)
            begin
                stop <= stop + 1;
                if (stop == 6) begin
                    $finish;
                end
            end
        
    endmodule
                
  • CONCLUSION
    • Results are in the PDF below.

DIFFICULTY:

SKILL:

INTEREST

  • SCOPE
    • The game will function as follows: On pressing the Start button (i.e., one of the four push buttons soldered onto the Boolean Board PCB), the LFSR module will begin generating a stream of 16-bit pseudo-random numbers. When the Roll button is pressed, each bit of the upper half of the LFSR output should be XOR'ed with the corresponding bit of the lower 8 bits of the LFSR output. Recall the first Hamming code lab for ideas on how to do this efficiently. The resulting stream of 8-bit values will be employed to provide the "chance" element of the game.
    • To reuse the most code possible, you will have the two random hex numbers displayed in real-time on the seven-segment display (on either the left two or the right two digits, similar to lab 2), and then display the win UI or lose LO notification on the remaining two digits. Read the following example to understand the proper operation of the game:
    • A sample play of the game is as follows:
      1. The player starts to generate random numbers. The player presses the Start/Reset button, so the random numbers are generated on two digits, just as in lab 2.
      2. The player then taps the Roll button. Sum up the current two hex numbers, if the sum is greater than 25, then UI is displayed, and the player wins. If the sum is less than 5, then LO is displayed (on the other remaining two digits), and the player loses. In both cases, the game is over. If neither of the above cases is hit, store the sum and proceed to the next step. (here, hex digits are represented by their 4-bit number, i.e., A=10, F=15)
      3. If the sum is between 5 and 25 (5 and 25 included), store the sum as "target" the player has to roll again, and 'Ao' is displayed. On pressing the roll button, take the sum. If the new sum is less than the target, roll again. If the new sum is greater than or equal to the target but less than or equal to 25, the player wins, and if it is greater than 25, the player loses.
      4. The UI or LO is still displayed until the start button's next tap.
    • For simplicity, LO can be displayed before the first tap of the roll button.
    • Of course, the player can always see the value of the hex digits and thus tap the Roll button at the right time to win or lose the game, but this is done for debugging purposes.
  • CODE — fsm.vg
  • 
    `timescale 1ns / 1ps
    module fsm(
        input  wire [3:0]  i4lButton,     // [0]=Start/Reset, [1]=Roll
        input  wire [15:0] b16lPRN,       // Random 16-bit number
        input  wire        isClk, 
        output reg  [15:0] char_select,   // Characters ("UI", "LO", "AO")
        output reg  [2:0]  game_decision, // WIN, LOSE, AO
        output reg  [15:0] decision_led,  // WIN, LOSE, AO
        output reg  [7:0]  SUM,
        output reg  [7:0]  target         // Expose target to TB
    );
    
        reg [1:0] current_state, next_state;
        reg [7:0] next_target;
        reg [7:0] next_SUM;
        reg [2:0] next_game_decision;
        reg [15:0] next_char_select;
    
        // State encoding
        localparam [1:0] IDLE = 2'b00, ROLL = 2'b01, DONE = 2'b11;
    
        // Game decisions
        localparam [2:0] WIN  = 3'b100, LOSE = 3'b010, AO = 3'b110;
    
        // Characters
        localparam [7:0] A = 8'b10001000, o = 8'b10100011,
                          U = 8'b11000001, I = 8'b11111001,
                          L = 8'b11000111, O = 8'b11000000;
    
        wire [7:0] SUM_W = b16lPRN[15:8] + b16lPRN[7:0];
    
        // Sequential block
        always @(posedge isClk) begin
            if (i4lButton[0]) begin
                // RESET
                current_state <= IDLE;
                SUM <= 0;
                target <= 0;
                game_decision <= LOSE;
                char_select <= {L,O};
                decision_led <= 16'd0;
            end else begin
                current_state <= next_state;
                SUM <= next_SUM;
                target <= next_target;
                game_decision <= next_game_decision;
                char_select <= next_char_select;
            end
        end
    
        // Combinational block
        always @(*) begin
            // defaults: keep previous values
            next_state = current_state;
            next_target = target;
            next_SUM = SUM;                     // default: SUM frozen
            next_game_decision = game_decision;
            next_char_select = char_select;
    
            case(current_state)
                IDLE: begin
                    next_SUM = SUM_W;            // display RNG in IDLE
                    if (i4lButton[1]) begin
                        if (SUM_W > 25) begin
                            next_state = DONE;
                            next_game_decision = WIN;
                            next_char_select = {U,I};
                            next_SUM = SUM_W;   // latch SUM at button press
                        end else if (SUM_W < 5) begin
                            next_state = DONE;
                            next_game_decision = LOSE;
                            next_char_select = {L,O};
                            next_SUM = SUM_W;
                        end else begin
                            next_state = ROLL;
                            next_target = SUM_W;
                            next_game_decision = AO;
                            next_char_select = {A,o};
                            next_SUM = SUM_W;   // latch SUM
                        end
                    end
                end
    
                ROLL: begin
                    if (i4lButton[1]) begin
                        next_SUM = SUM_W;  // latch new roll each button press
                        if (SUM_W > 25) begin
                            next_state = DONE;
                            next_game_decision = LOSE;
                            next_char_select = {L,O};
                        end else if (SUM_W >= target) begin
                            next_state = DONE;
                            next_game_decision = WIN;
                            next_char_select = {U,I};
                        end else begin
                            next_state = ROLL;
                            next_game_decision = AO;
                            next_char_select = {A,o};
                            // target remains the same
                        end
                    end
                end
                
    
                DONE: begin
                    if (i4lButton[0]) begin
                        next_state = IDLE;
                        next_game_decision = LOSE;
                        next_char_select = {L,O};
                        next_target = 0;
                        next_SUM = 0;
                    end
                end
    
                default: next_state = IDLE;
            endcase
        end
    
    endmodule
                
  • CODE — fsm_tb.v
  • 
    `timescale 1ns / 1ps
    module fsm_tb ();
    
        // Inputs
        reg        isClk;
        reg [3:0]  i4lButton;
        reg [15:0] b16lPRN;
    
        // Outputs
        wire [15:0] char_select;
        wire [2:0]  game_decision;
        wire [7:0]  SUM;
        wire [7:0] target;
    
        // Instantiate DUT
        fsm DUT (
            .i4lButton(i4lButton),
            .b16lPRN(b16lPRN),
            .isClk(isClk),
            .char_select(char_select),
            .game_decision(game_decision),
            .SUM(SUM),
            .target(target)
        );
    
        // 10 ns clock
        initial begin
            isClk = 0;
            forever #5 isClk = ~isClk;
        end
    
        // Helper pulses
        task pulse_start; begin
            i4lButton[0] = 1; #15;
            i4lButton[0] = 0; #15;
        end endtask
    
        task pulse_roll; begin
            i4lButton[1] = 1; #15;
            i4lButton[1] = 0; #15;
        end endtask
    
        // Function to compute SUM from PRN
        function integer sum_now(input [15:0] x);
            sum_now = x[15:8] + x[7:0];
        endfunction
    
        // Testbench sequence
        initial begin
            i4lButton = 0;
            b16lPRN   = 0;
    
            $display("\nStarting Testbench\n");
    
            // 1) RESET / Initial state
            pulse_start(); #20;
            $display("1) RESET: char=%h, decision=%b, SUM=%0d, target=%d",
                      char_select, game_decision, SUM, target);
    
            // 2) Immediate WIN (SUM>25)
            b16lPRN = {8'd20, 8'd10}; // SUM = 30
            pulse_roll(); #20;
            $display("2) Immediate WIN SUM=%0d: char=%h, decision=%b, target=%d",
                      sum_now(b16lPRN), char_select, game_decision, target);
    
            // 3) Immediate LOSE (SUM<5)
            pulse_start(); #20;
            b16lPRN = {8'd1,8'd2}; // SUM=3
            pulse_roll(); #20;
            $display("3) Immediate LOSE SUM=%0d: char=%h, decision=%b, target=%d",
                      sum_now(b16lPRN), char_select, game_decision, target);
    
            // 4) Enter ROLL (SUM in 5-25)
            pulse_start(); #20;
            b16lPRN = {8'd10,8'd10}; // SUM=20
            pulse_roll(); #20;
            $display("4) Enter ROLL 1 SUM=%0d: char=%h, decision=%b, target=%d",
                      sum_now(b16lPRN), char_select, game_decision, target);
    
            b16lPRN = {8'd15,8'd8}; // SUM=23, target=20
            pulse_roll(); #20;
            $display("   ROLL 2 -> WIN SUM=%0d: char=%h, decision=%b, target=%d",
                      sum_now(b16lPRN), char_select, game_decision, target);
    
            pulse_start(); #20;
            b16lPRN = {8'd12,8'd10}; // SUM=22 enter ROLL
            pulse_roll(); #20;
            $display("5) ROLL 1 -> LOSE SUM=%0d: char=%h, decision=%b, target=%d",
                      sum_now(b16lPRN), char_select, game_decision, target);
            b16lPRN = {8'd60,8'd10}; // SUM=70
            pulse_roll(); #20;
            $display("   ROLL 2 -> LOSE SUM=%0d: char=%h, decision=%b, target=%d",
                      sum_now(b16lPRN), char_select, game_decision, target);
    
            // 7) ROLL -> AO (SUM < target)
            pulse_start(); #20;
            b16lPRN = {8'd10,8'd10}; // SUM=20 enter ROLL
            pulse_roll(); #20;
            $display("6) ROLL 1 -> AO SUM=%0d: char=%h, decision=%b, target=%d",
                      sum_now(b16lPRN), char_select, game_decision, target);
            b16lPRN = {8'd15,8'd4}; // SUM=19 < target=20
            pulse_roll(); #20;
            $display("   ROLL 2 -> AO SUM=%0d: char=%h, decision=%b, target=%d",
                      sum_now(b16lPRN), char_select, game_decision, target);
            b16lPRN = {8'd20,8'd6}; // SUM=26 < target=19
            pulse_roll(); #20;
            $display("   ROLL 3 -> LOSE SUM=%0d: char=%h, decision=%b, target=%d",
                      sum_now(b16lPRN), char_select, game_decision, target);
    
            // 8) RESET during DONE
            pulse_start(); #20;
            $display("7) RESET during DONE: char=%h, decision=%b, SUM=%0d, target=%d",
                      char_select, game_decision, SUM, target);
    
            $display("\nEnding Testbench\n");
            #100;
            $finish;
        end
    
    endmodule
                
  • CODE — top.vg
  • 
    `timescale 1ns / 1ps
    
    module top (
        output [15:0] o16lLED,
        output wire [ 7:0] o8lSSeg0Cathode,
        output reg [ 3:0] o4lSSeg0Anode,
        input         isClk,
        input  [ 3:0] i4lButton,
        input  [15:0] i16lSwitch
    );
        // Clock signal
        wire osClk1Hz;       
        wire osClk10Hz;       
        wire osClk7seg;      
    
        wire [7:0] SUM; // XOR'd Result of the random number
        wire [15:0] b16lPRN;
        reg [3:0] current_digit; //
        reg [7:0] current_digit2; //
        reg [1:0] digit_select = 0;
    
        wire [15:0] char_select;
        reg use_char  =  1'b0;
      
                
        // i4lButton[0] is the reset button and i4lButton[1] is the Roll button
        lfsr lfsr (
            .b16lPRN(b16lPRN),
            .i16lSeed(i16lSwitch),
            .isRst(i4lButton[0]), // Button to Start/Reset the seed 
            .isClk(osClk1Hz)
        );
        
        clock_divider clk_div1Hz (
            .osClk1Hz(osClk1Hz),
            .isClk100MHz(isClk),
            .divisor(100_000_000)
        );
        
        clock_divider clk_div10Hz (
            .osClk1Hz(osClk10Hz),
            .isClk100MHz(isClk),
            .divisor(50_000_000)
        );
    
        clock_divider clk_div7seg (
            .osClk1Hz(osClk7seg),
            .isClk100MHz(isClk),
            .divisor(50_000)   // NEW: 2 kHz refresh
        );
    
        decoder seg_decoder (
            .o8lSSeg(o8lSSeg0Cathode),
            .i8lHexDigit(current_digit),
            .i8lBinDigit(current_digit2),
            .use_char(use_char)
        );
        
        fsm fsm(
            .i4lButton(i4lButton),
            .b16lPRN(b16lPRN),
            .char_select(char_select),
            .isClk(osClk10Hz),
            .SUM(SUM)
        );
    
        assign o16lLED = b16lPRN;
        
      // Multiplexing logic for 7-segment display
        always @(posedge osClk7seg) begin
            case (digit_select)
                2'b00: begin
                    use_char <= 1'b0;
                    current_digit <= {4'b0, SUM[3:0]};
                    o4lSSeg0Anode <= 4'b1110;
                end
                2'b01: begin
                    use_char <= 1'b0;
                    current_digit <= {4'b0, SUM[7:4]};
                    o4lSSeg0Anode <= 4'b1101;
                end
                2'b10: begin
                    use_char <= 1'b1;
                    current_digit2 <=  char_select[7:0];
                    o4lSSeg0Anode <= 4'b1011;
                end
                2'b11: begin
                    use_char <= 1'b1;
                    current_digit2 <= char_select[15:8];
                    o4lSSeg0Anode <= 4'b0111;
                end
            endcase
            digit_select <= digit_select + 1;
        end
    
    endmodule              
                
  • CODE — top_tb.v
  • 
    `timescale 1ns / 1ps
    
    module top_tb ();
    
        reg        isClk;
        reg [3:0]  i4lButton;
        reg [15:0] b16lPRN;
        wire [15:0] char_select;
        wire [1:0]  game_decision;
        wire [7:0]  SUM;
    
        fsm DUT (
            .i4lButton(i4lButton),
            .b16lPRN(b16lPRN),
            .isClk(isClk),
            .char_select(char_select),
            .game_decision(game_decision),
            .SUM(SUM)
        );
    
        // System clock (10ns period)
        initial begin
            isClk = 0;
            forever #5 isClk = ~isClk;
        end
    
        // Proper pulse (≥3 cycles)
        task pulse_start;
        begin
            i4lButton[0] = 0;
            #40;
            i4lButton[0] = 1;
            #40;
        end
        endtask
    
        task pulse_roll;
        begin
            i4lButton[1] = 0;
            #40;
            i4lButton[1] = 1;
            #40;
        end
        endtask
    
        // Helper: print SUM immediately from PRN
        function integer sum_now;
            input [15:0] x;
            begin
                sum_now = x[15:8] + x[7:0];
            end
        endfunction
    
    
        initial begin
            i4lButton = 0;
            b16lPRN   = 0;
    
            $display("\n Starting Testbench \n");
    
            // TEST 1 : RESET
            pulse_start();
            #20;
            $display("1) Reset test char=%h  decision=%b (Expect LO Initial)",
                      char_select, game_decision);
            $display("  Stored target = %0d\n", DUT.target);
    
            // TEST 2 : WIN immediately
            b16lPRN = {8'd20, 8'd10}; // SUM = 30
            $display("2) SUM > 25 immediate win. SUM=%0d", sum_now(b16lPRN));
            pulse_roll();
            #20;
            $display("  Stored target = %0d", DUT.target);
            $display("  Result char=%h decision=%b (WIN expected)\n",
                      char_select, game_decision);
    
            // RESET AGAIN
            pulse_start();
            #20;
    
            // TEST 3 : LOSE immediately
            b16lPRN = {8'd1, 8'd2}; // SUM = 3
            $display("3) SUM < 5 immediate loss. SUM=%0d", sum_now(b16lPRN));
            pulse_roll();
            #20;
            $display("  Stored target = %0d", DUT.target);
            $display("  Result char=%h decision=%b (LOSE expected)\n",
                      char_select, game_decision);
    
            // RESET AGAIN
            pulse_start();
            #20;
    
            // TEST 4 : ENTER ROLL state (correct test)
            b16lPRN = {8'd10, 8'd10}; // SUM = 20
            $display("4) Enter ROLL: SUM=%0d (5-25 OK)", sum_now(b16lPRN));
            pulse_roll();
            #20;
            $display("  Stored target (should be 20) = %0d\n", DUT.target);
    
            // Now WIN if SUM >= target
            b16lPRN = {8'd15, 8'd6}; // SUM = 20
            $display("   Roll WIN test: SUM=%0d vs target=%0d",
                      sum_now(b16lPRN), DUT.target);
            pulse_roll();
            #20;
            $display("  Result char=%h decision=%b (WIN expected)\n",
                      char_select, game_decision);
    
            // RESET AGAIN
            pulse_start();
            #20;
    
            // TEST 5 : ENTER ROLL then LOSE
            b16lPRN = {8'd12, 8'd10}; // SUM = 22
            $display("5) Enter ROLL: SUM=%0d", sum_now(b16lPRN));
            pulse_roll();
            #20;
            $display("  Stored target = %0d", DUT.target);
    
            // Now LOSE with SUM > 25
            b16lPRN = {8'd60, 8'd10}; // SUM = 70
            $display("   Roll LOSE test: SUM=%0d vs target=%0d",
                      sum_now(b16lPRN), DUT.target);
            pulse_roll();
            #20;
            $display("  Result char=%h decision=%b (LOSE expected)\n",
                      char_select, game_decision);
                      
                      
            pulse_start();
            #20;
    
            // TEST 6 : ENTER ROLL state (correct test)
            b16lPRN = {8'd10, 8'd10}; // SUM = 20
            $display("6) Enter ROLL: SUM=%0d (5-25 OK)", sum_now(b16lPRN));
            pulse_roll();
            #20;
            $display("  Stored target (should be 20) = %0d\n", DUT.target);
    
            // Now WIN if SUM >= target
            b16lPRN = {8'd15, 8'd4}; // SUM = 19
            $display("   Roll AO test: SUM=%0d vs target=%0d",
                      sum_now(b16lPRN), DUT.target);
            pulse_roll();
            #20;
            $display("  Stored target (should be 20) = %0d\n", DUT.target);
            
            b16lPRN = {8'd15, 8'd6}; // SUM = 20
            $display("  Result char=%h decision=%b (WIN expected)\n",
                      char_select, game_decision);
    
    
            // END
            $display("\n Ending Testbench \n");
            #100;
            $finish;
        end
    
    endmodule              
                
  • CONCLUSION

DIFFICULTY:

SKILL:

INTEREST