Repository: ShawnHymel/introduction-to-fpga Branch: main Commit: 79d295449987 Files: 116 Total size: 104.9 KB Directory structure: gitextract_qazi7qst/ ├── .gitignore ├── 02-install-apio/ │ └── solution-turn-off-led/ │ ├── apio.ini │ ├── info │ ├── leds.pcf │ ├── leds.v │ ├── leds_tb.gtkw │ └── leds_tb.v ├── 03-verilog-gate-logic/ │ ├── example-01-and-gate/ │ │ ├── and_gate.pcf │ │ ├── and_gate.v │ │ └── apio.ini │ ├── example-02-vectors/ │ │ ├── apio.ini │ │ ├── vectors.pcf │ │ └── vectors.v │ └── solution-full-adder/ │ ├── apio.ini │ ├── full-adder.pcf │ └── full-adder.v ├── 04-clocks-and-procedural-assignments/ │ ├── example-01-button-counter/ │ │ ├── apio.ini │ │ ├── button-counter.pcf │ │ └── button-counter.v │ └── solution-clock-counter/ │ ├── apio.ini │ ├── clock-counter.pcf │ └── clock-counter.v ├── 05-finite-state-machines/ │ ├── example-01-moore-fsm/ │ │ ├── apio.ini │ │ ├── moore-fsm.pcf │ │ └── moore-fsm.v │ ├── example-02-mealy-fsm/ │ │ ├── apio.ini │ │ ├── mealy-fsm.pcf │ │ └── mealy-fsm.v │ └── solution-button-debouncing/ │ ├── apio.ini │ ├── solution-button-debouncing.pcf │ └── solution-button-debouncing.v ├── 06-modules-and-parameters/ │ ├── example-01-parameters/ │ │ ├── apio.ini │ │ ├── clock-divider.v │ │ ├── top-design.pcf │ │ └── top-design.v │ ├── example-02-ansi-parameters/ │ │ ├── apio.ini │ │ ├── clock-divider.v │ │ ├── top-design.pcf │ │ └── top-design.v │ └── solution-count-up-down/ │ ├── apio.ini │ ├── clock-divider.v │ ├── count-up-down-top.v │ ├── count-up-down.pcf │ └── counter-fsm.v ├── 07-simulation-and-testbenches/ │ ├── example-01-testbench/ │ │ ├── apio.ini │ │ ├── clk-div_tb.gtkw │ │ ├── clk-div_tb.v │ │ └── clock-divider.v │ └── solution-debounce-testbench/ │ ├── apio.ini │ ├── button-debouncing.v │ ├── button-debouncing_tb.gtkw │ └── button-debouncing_tb.v ├── 08-memory/ │ ├── example-01-memory/ │ │ ├── apio.ini │ │ ├── memory.v │ │ ├── memory_tb.gtkw │ │ ├── memory_tb.v │ │ └── test.pcf │ ├── example-02-memory-initialization/ │ │ ├── apio.ini │ │ ├── mem_init.txt │ │ ├── memory.v │ │ ├── memory_tb.gtkw │ │ ├── memory_tb.v │ │ └── test.pcf │ └── solution-sequencer/ │ ├── apio.ini │ ├── clock-divider.v │ ├── debouncer.v │ ├── mem_init.txt │ ├── memory.v │ ├── sequencer-top.pcf │ ├── sequencer-top.v │ ├── sequencer-top_tb.gtkw │ └── sequencer-top_tb.v ├── 09-pll-and-glitches/ │ ├── example-01-pll/ │ │ ├── apio.ini │ │ ├── pll_test.pcf │ │ └── pll_test.v │ ├── example-02-glitches/ │ │ ├── apio.ini │ │ ├── empty.v │ │ ├── glitch-test_tb.gtkw │ │ └── glitch-test_tb.v │ └── solution-gray-code-counter/ │ ├── apio.ini │ ├── empty.v │ ├── gray-code-counter_tb.gtkw │ └── gray-code-counter_tb.v ├── 10-metastability/ │ ├── example-01-metastability-test/ │ │ ├── apio.ini │ │ ├── dual-sqwv.dwf3work │ │ ├── metastability-test.pcf │ │ └── metastability-test.v │ ├── example-02-better-clock-divider/ │ │ ├── apio.ini │ │ ├── clock-divider.v │ │ ├── clock-divider_tb.gtkw │ │ ├── clock-divider_tb.v │ │ ├── slow-blink.pcf │ │ └── slow-blink.v │ ├── example-03-better-debouncer/ │ │ ├── apio.ini │ │ ├── debouncer.v │ │ ├── debouncer_tb.gtkw │ │ └── debouncer_tb.v │ └── solution-async-fifo/ │ ├── apio.ini │ ├── async-fifo.v │ ├── async-fifo_tb.gtkw │ ├── async-fifo_tb.v │ ├── dummy.pcf │ ├── r-ptr-empty.v │ └── w-ptr-full.v ├── 11-risc-v-softcore-cpu/ │ ├── example-01-blinky/ │ │ ├── Makefile │ │ └── main.c │ └── solution-buttons/ │ ├── Makefile │ └── main.c ├── 12-risc-v-custom-peripheral/ │ └── example-01-pwm/ │ ├── C/ │ │ └── pwm_test/ │ │ ├── Makefile │ │ └── main.c │ └── RTL/ │ ├── apio.ini │ ├── pwm.pcf │ ├── pwm.v │ ├── pwm_tb.gtkw │ └── pwm_tb.v └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # yosys - Verilog synthesis # nextpnr - Place and route # Icarus Verilog - Simulation # GTKWate - Simulation viewer *.dblite *.history *.asc *.bin *.json *.out *.vcd *.out ================================================ FILE: 02-install-apio/solution-turn-off-led/apio.ini ================================================ [env] board = icestick ================================================ FILE: 02-install-apio/solution-turn-off-led/info ================================================ Hello world example for the Icestick board. Turning all the leds on ================================================ FILE: 02-install-apio/solution-turn-off-led/leds.pcf ================================================ # ----------------------------------------------------------------------------- #- Leds example for the iCEstick board #- Constraint file (.pcf) # ----------------------------------------------------------------------------- # -- Pinout: https://github.com/Obijuan/open-fpga-verilog-tutorial/blob/master/tutorial/doc/images/icestick_pinout.png # -- Guide: https://github.com/Obijuan/open-fpga-verilog-tutorial/blob/master/tutorial/doc/icestickusermanual.pdf # ------------ User Leds ------------------------------------------------------ set_io D1 99 set_io D2 98 set_io D3 97 set_io D4 96 set_io D5 95 ================================================ FILE: 02-install-apio/solution-turn-off-led/leds.v ================================================ //------------------------------------------------------------------ //-- Hello world example for the iCEstick board //-- Turn on all the leds //------------------------------------------------------------------ module leds(output wire D1, output wire D2, output wire D3, output wire D4, output wire D5); assign D1 = 1'b1; assign D2 = 1'b1; assign D3 = 1'b1; assign D4 = 1'b1; assign D5 = 1'b0; endmodule ================================================ FILE: 02-install-apio/solution-turn-off-led/leds_tb.gtkw ================================================ [*] [*] GTKWave Analyzer v3.3.66 (w)1999-2015 BSI [*] Mon Aug 29 09:12:21 2016 [*] [dumpfile] "/home/jesus/code/apio-examples/icestick-leds/leds_tb.vcd" [dumpfile_mtime] "Mon Aug 29 09:10:49 2016" [dumpfile_size] 606 [savefile] "/home/jesus/code/apio-examples/icestick-leds/leds_tb.gtkw" [timestart] 0 [size] 1000 600 [pos] 507 137 *0.000000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [treeopen] leds_tb. [sst_width] 223 [signals_width] 78 [sst_expanded] 1 [sst_vpaned_height] 160 @28 leds_tb.UUT.D1 leds_tb.UUT.D2 leds_tb.UUT.D3 leds_tb.UUT.D4 @29 leds_tb.UUT.D5 [pattern_trace] 1 [pattern_trace] 0 ================================================ FILE: 02-install-apio/solution-turn-off-led/leds_tb.v ================================================ //------------------------------------------------------------------- //-- leds_tb.v //-- Testbench //------------------------------------------------------------------- //-- Juan Gonzalez (Obijuan) //-- Jesus Arroyo Torrens //-- GPL license //------------------------------------------------------------------- `default_nettype none `define DUMPSTR(x) `"x.vcd`" `timescale 100 ns / 10 ns module leds_tb(); //-- Simulation time: 1us (10 * 100ns) parameter DURATION = 10; //-- Clock signal. It is not used in this simulation reg clk = 0; always #0.5 clk = ~clk; //-- Leds port wire d1, d2, d3, d4, d5; //-- Instantiate the unit to test leds UUT ( .D1(d1), .D2(d2), .D3(d3), .D4(d4), .D5(d5) ); initial begin //-- File were to store the simulation results $dumpfile(`DUMPSTR(`VCD_OUTPUT)); $dumpvars(0, leds_tb); #(DURATION) $display("End of simulation"); $finish; end endmodule ================================================ FILE: 03-verilog-gate-logic/example-01-and-gate/and_gate.pcf ================================================ # LEDs set_io led_0 99 # PMOD I/O set_io -pullup yes pmod_0 78 set_io -pullup yes pmod_1 79 ================================================ FILE: 03-verilog-gate-logic/example-01-and-gate/and_gate.v ================================================ // AND gate example // // Inputs: // pmod_0 - pushbutton // pmod_1 - pushbutton // Outputs: // led_0 - LED // // LED will turn on if both pushbuttons are pressed (~pmod_0 & ~pmod_1). // // Date: October 13, 2021 // Author: Shawn Hymel // License: 0BSD // Module: when buttons 1 and 2 are pressed, turn on LED module and_gate ( // Inputs input pmod_0, input pmod_1, // Outputs output led_0 ); // Continuous assignment: NOT and AND operators assign led_0 = ~pmod_0 & ~pmod_1; endmodule ================================================ FILE: 03-verilog-gate-logic/example-01-and-gate/apio.ini ================================================ [env] board = icestick ================================================ FILE: 03-verilog-gate-logic/example-02-vectors/apio.ini ================================================ [env] board = icestick ================================================ FILE: 03-verilog-gate-logic/example-02-vectors/vectors.pcf ================================================ # LEDs set_io led[0] 99 set_io led[1] 98 set_io led[2] 97 set_io led[3] 96 set_io led[4] 95 # PMOD I/O set_io -pullup yes pmod[0] 78 set_io -pullup yes pmod[1] 79 ================================================ FILE: 03-verilog-gate-logic/example-02-vectors/vectors.v ================================================ // Vector and intermediate (wire) value example // // Inputs: // pmod[1:0] - pushbuttons (x2) // // Outputs: // led[2:0] - LEDs (x3) // // LEDs 0 and 1 turn on if pmod[0] button is pressed. LED 2 turns on if pmod[0] // and pmod[1] are pressed together. // // Date: October 13, 2021 // Author: Shawn Hymel // License: 0BSD // Module: button 0 lights up 2 LEDs, button 0 and 1 light up another LED module and_gate ( // Inputs input [1:0] pmod, // Output output [2:0] led ); // Wire (net) declarations (internal to module) wire not_pmod_0; // Continuous assignment: replicate 1 wire to 2 outputs assign not_pmod_0 = ~pmod[0]; assign led[1:0] = {2{not_pmod_0}}; // Continuous assignment: NOT and AND operators assign led[2] = not_pmod_0 & ~pmod[1]; endmodule ================================================ FILE: 03-verilog-gate-logic/solution-full-adder/apio.ini ================================================ [env] board = icestick ================================================ FILE: 03-verilog-gate-logic/solution-full-adder/full-adder.pcf ================================================ # LEDs set_io led[0] 99 set_io led[1] 98 # PMOD I/O set_io -pullup yes pmod[0] 78 set_io -pullup yes pmod[1] 79 set_io -pullup yes pmod[2] 80 ================================================ FILE: 03-verilog-gate-logic/solution-full-adder/full-adder.v ================================================ // Solution to full adder challenge // // Inputs: // pmod[2:0] - pushbuttons (x3) // // Outputs: // led[1:0] - LEDs (x2) // // LED 0 turns on if 1 or 3 buttons are pressed. LED 1 turns on if 2 or 3 // buttons are pressed. // // Date: October 25, 2021 // Author: Shawn Hymel // License: 0BSD // Full adder with button inputs module full_adder ( // Inputs input [2:0] pmod, // Output output [1:0] led ); // Wire (net) declarations (internal to module) wire a; wire b; wire c_in; wire a_xor_b; // A, B, and C_IN are inverted button logic assign a = ~pmod[0]; assign b = ~pmod[1]; assign c_in = ~pmod[2]; // Create intermediate wire (net) assign a_xor_b = a ^ b; // Create output logic assign led[0] = a_xor_b ^ c_in; assign led[1] = (a_xor_b & c_in) | (a & b); endmodule ================================================ FILE: 04-clocks-and-procedural-assignments/example-01-button-counter/apio.ini ================================================ [env] board = icestick ================================================ FILE: 04-clocks-and-procedural-assignments/example-01-button-counter/button-counter.pcf ================================================ # LEDs set_io led[0] 99 set_io led[1] 98 set_io led[2] 97 set_io led[3] 96 # PMOD I/O set_io -pullup yes pmod[0] 78 set_io -pullup yes pmod[1] 79 ================================================ FILE: 04-clocks-and-procedural-assignments/example-01-button-counter/button-counter.v ================================================ // Button counter // // Inputs: // pmod[0] - pushbutton (RESET) // pmod[1] - pushbutton (CLOCK) // Outputs: // led[3:0] - LEDs (count from 0x0 to 0xf) // // LEDs will count up in binary on each CLOCK button press. Note: there will // be a lot of button bounce, so don't worry if the numbers seem to skip around! // // Date: October 26, 2021 // Author: Shawn Hymel // License: 0BSD // Count up on each button press and display on LEDs module button_counter ( // Inputs input [1:0] pmod, // Outputs output reg [3:0] led ); wire rst; wire clk; // Reset is the inverse of the first button assign rst = ~pmod[0]; // Clock signal is the inverse of second button assign clk = ~pmod[1]; // Count up on clock rising edge or reset on button push always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin led <= 4'b0; end else begin led <= led + 1'b1; end end endmodule ================================================ FILE: 04-clocks-and-procedural-assignments/solution-clock-counter/apio.ini ================================================ [env] board = icestick ================================================ FILE: 04-clocks-and-procedural-assignments/solution-clock-counter/clock-counter.pcf ================================================ # Oscillator set_io clk 21 # LEDs set_io led[0] 99 set_io led[1] 98 set_io led[2] 97 set_io led[3] 96 # PMOD I/O set_io -pullup yes rst_btn 78 ================================================ FILE: 04-clocks-and-procedural-assignments/solution-clock-counter/clock-counter.v ================================================ // Clock-divided counter // // Inputs: // clk - 12 MHz clock // rst_btn - pushbutton (RESET) // // Outputs: // led[3:0] - LEDs (count from 0x0 to 0xf) // // LEDs will display a binary number that increments by one each second. // // Date: October 26, 2021 // Author: Shawn Hymel // License: 0BSD // Count up each second module clock_counter ( // Inputs input clk, input rst_btn, // Outputs output reg [3:0] led ); wire rst; reg div_clk; reg [31:0] count; localparam [31:0] max_count = 6000000 - 1; // Reset is the inverse of the reset button assign rst = ~rst_btn; // Count up on (divided) clock rising edge or reset on button push always @ (posedge div_clk or posedge rst) begin if (rst == 1'b1) begin led <= 4'b0; end else begin led <= led + 1'b1; end end // Clock divider always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin count <= 32'b0; end else if (count == max_count) begin count <= 32'b0; div_clk <= ~div_clk; end else begin count <= count + 1; end end endmodule ================================================ FILE: 05-finite-state-machines/example-01-moore-fsm/apio.ini ================================================ [env] board = icestick ================================================ FILE: 05-finite-state-machines/example-01-moore-fsm/moore-fsm.pcf ================================================ # Oscillator set_io clk 21 # LEDs set_io led[0] 99 set_io led[1] 98 set_io led[2] 97 set_io led[3] 96 set_io done_sig 95 # PMOD I/O set_io -pullup yes rst_btn 78 set_io -pullup yes go_btn 79 ================================================ FILE: 05-finite-state-machines/example-01-moore-fsm/moore-fsm.v ================================================ // Count up on button press and stop (Moore state machine) // // Inputs: // clk - 12 MHz clock // rst_btn - pushbutton (RESET) // go_btn - pushbutton (GO) // // Outputs: // led[3:0] - LEDs (count from 0x0 to 0xf) // done_sig - LED that flashes once when counting is done_sig // // Press GO button to start counting. When max value (0xf) is reached, green // LED will flash once, and the state machine will return to the Idle state. // // Date: November 4, 2021 // Author: Shawn Hymel // License: 0BSD // State machine that counts up when button is pressed module moore_fsm ( // Inputs input clk, input rst_btn, input go_btn, // Outputs output reg [3:0] led, output reg done_sig ); // States localparam STATE_IDLE = 2'd0; localparam STATE_COUNTING = 2'd1; localparam STATE_DONE = 2'd2; // Max counts for clock divider and counter localparam MAX_CLK_COUNT = 24'd1500000; localparam MAX_LED_COUNT = 4'hF; // Internal signals wire rst; wire go; // Internal storage elements reg div_clk; reg [1:0] state; reg [23:0] clk_count; // Invert active-low buttons assign rst = ~rst_btn; assign go = ~go_btn; // Clock divider always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin clk_count <= 24'b0; end else if (clk_count == MAX_CLK_COUNT) begin clk_count <= 24'b0; div_clk <= ~div_clk; end else begin clk_count <= clk_count + 1; end end // State transition logic always @ (posedge div_clk or posedge rst) begin // On reset, return to idle state if (rst == 1'b1) begin state <= STATE_IDLE; // Define the state transitions end else begin case (state) // Wait for go button to be pressed STATE_IDLE: begin if (go == 1'b1) begin state <= STATE_COUNTING; end end // Go from counting to done if counting reaches max STATE_COUNTING: begin if (led == MAX_LED_COUNT) begin state <= STATE_DONE; end end // Spend one clock cycle in done state STATE_DONE: state <= STATE_IDLE; // Go to idle if in unknown state default: state <= STATE_IDLE; endcase end end // Handle the LED counter always @ (posedge div_clk or posedge rst) begin if (rst == 1'b1) begin led <= 4'd0; end else begin if (state == STATE_COUNTING) begin led <= led + 1; end else begin led <= 4'd0; end end end // Handle done LED output always @ ( * ) begin if (state == STATE_DONE) begin done_sig = 1'b1; end else begin done_sig = 1'b0; end end endmodule ================================================ FILE: 05-finite-state-machines/example-02-mealy-fsm/apio.ini ================================================ [env] board = icestick ================================================ FILE: 05-finite-state-machines/example-02-mealy-fsm/mealy-fsm.pcf ================================================ # Oscillator set_io clk 21 # LEDs set_io led[0] 99 set_io led[1] 98 set_io led[2] 97 set_io led[3] 96 set_io done_sig 95 # PMOD I/O set_io -pullup yes rst_btn 78 set_io -pullup yes go_btn 79 ================================================ FILE: 05-finite-state-machines/example-02-mealy-fsm/mealy-fsm.v ================================================ // Count up on button press and stop (Mealy state machine) // // Inputs: // clk - 12 MHz clock // rst_btn - pushbutton (RESET) // go_btn - pushbutton (GO) // // Outputs: // led[3:0] - LEDs (count from 0x0 to 0xf) // done_sig - LED that flashes once when counting is done_sig // // Press GO button to start counting. When max value (0xf) is reached, green // LED will flash once, and the state machine will return to the Idle state. // // Date: November 4, 2021 // Author: Shawn Hymel // License: 0BSD // State machine that counts up when button is pressed module mealy_fsm ( // Inputs input clk, input rst_btn, input go_btn, // Outputs output reg [3:0] led, output reg done_sig ); // States localparam STATE_IDLE = 1'd0; localparam STATE_COUNTING = 1'd1; // Max counts for clock divider and counter localparam MAX_CLK_COUNT = 24'd1500000; localparam MAX_LED_COUNT = 4'hF; // Internal signals wire rst; wire go; // Internal storage elements reg div_clk; reg state; reg [23:0] clk_count; // Invert active-low buttons assign rst = ~rst_btn; assign go = ~go_btn; // Clock divider always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin clk_count <= 24'b0; end else if (clk_count == MAX_CLK_COUNT) begin clk_count <= 24'b0; div_clk <= ~div_clk; end else begin clk_count <= clk_count + 1; end end // State transition logic always @ (posedge div_clk or posedge rst) begin // On reset, return to idle state if (rst == 1'b1) begin state <= STATE_IDLE; // Define the state transitions end else begin case (state) // Wait for go button to be pressed STATE_IDLE: begin done_sig <= 1'b0; if (go == 1'b1) begin state <= STATE_COUNTING; end end // Go from counting to done if counting reaches max STATE_COUNTING: begin if (led == MAX_LED_COUNT) begin done_sig <= 1'b1; state <= STATE_IDLE; end end // Go to idle if in unknown state default: state <= STATE_IDLE; endcase end end // Handle the LED counter always @ (posedge div_clk or posedge rst) begin if (rst == 1'b1) begin led <= 4'd0; end else begin if (state == STATE_COUNTING) begin led <= led + 1; end else begin led <= 4'd0; end end end endmodule ================================================ FILE: 05-finite-state-machines/solution-button-debouncing/apio.ini ================================================ [env] board = icestick ================================================ FILE: 05-finite-state-machines/solution-button-debouncing/solution-button-debouncing.pcf ================================================ # Oscillator set_io clk 21 # LEDs set_io led[0] 99 set_io led[1] 98 set_io led[2] 97 set_io led[3] 96 # PMOD I/O set_io -pullup yes rst_btn 78 set_io -pullup yes inc_btn 79 ================================================ FILE: 05-finite-state-machines/solution-button-debouncing/solution-button-debouncing.v ================================================ // One possible way to debounce a button press (using a Moore state machine) // // Inputs: // clk - 12 MHz clock // rst_btn - pushbutton (RESET) // inc_btn - pushbutton (INCREMENT) // // Outputs: // led[3:0] - LEDs (count from 0x0 to 0xf) // // One press of the increment button should correspond to one and only one // increment of the counter. Use a state machine to identify the edge on the // button line, wait 40 ms, and sample again to see if the button is still // pressed. // // Date: November 5, 2021 // Author: Shawn Hymel // License: 0BSD // Use a state machine to debounce the button, which increments a counter module debounced_counter ( // Inputs input clk, input rst_btn, input inc_btn, // Outputs output reg [3:0] led ); // States localparam STATE_HIGH = 2'd0; localparam STATE_LOW = 2'd1; localparam STATE_WAIT = 2'd2; localparam STATE_PRESSED = 2'd3; // Max counts for wait state (40 ms with 12 MHz clock) localparam MAX_CLK_COUNT = 20'd480000 - 1; // Internal signals wire rst; wire inc; // Internal storage elements reg [1:0] state; reg [19:0] clk_count; // Invert active-low buttons assign rst = ~rst_btn; assign inc = ~inc_btn; // State transition logic always @ (posedge clk or posedge rst) begin // On reset, return to idle state and restart counters if (rst == 1'b1) begin state <= STATE_HIGH; led <= 4'd0; // Define the state transitions end else begin case (state) // Wait for increment signal to go from high to low STATE_HIGH: begin if (inc == 1'b0) begin state <= STATE_LOW; end end // Wait for increment signal to go from low to high STATE_LOW: begin if (inc == 1'b1) begin state <= STATE_WAIT; end end // Wait for count to be done and sample button again STATE_WAIT: begin if (clk_count == MAX_CLK_COUNT) begin if (inc == 1'b1) begin state <= STATE_PRESSED; end else begin state <= STATE_HIGH; end end end // If button is still pressed, increment LED counter STATE_PRESSED: begin led <= led + 1; state <= STATE_HIGH; end // Default case: return to idle state default: state <= STATE_HIGH; endcase end end // Run counter if in wait state always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin clk_count <= 20'd0; end else begin if (state == STATE_WAIT) begin clk_count <= clk_count + 1; end else begin clk_count <= 20'd0; end end end endmodule ================================================ FILE: 06-modules-and-parameters/example-01-parameters/apio.ini ================================================ [env] board = icestick ================================================ FILE: 06-modules-and-parameters/example-01-parameters/clock-divider.v ================================================ // Simple clock divider // // Parameters: // COUNT_WIDTH - Bus width for counter // MAX_COUNT - Maximum value of counter // // Inputs: // clk - Input clock // rst - Reset signal // // Outputs: // out - Divided clock output // // Toggles out line at a divided rate given clk input. // // Date: November 8, 2021 // Author: Shawn Hymel // License: 0BSD // Clock divider module clock_divider ( // Inputs input clk, input rst, // Outputs output reg out ); // Parameters parameter COUNT_WIDTH = 24; parameter [COUNT_WIDTH:0] MAX_COUNT = 6000000 - 1; // Internal signals wire rst; reg div_clk; reg [COUNT_WIDTH:0] count; // Clock divider always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin count <= 0; out <= 0; end else if (count == MAX_COUNT) begin count <= 0; out <= ~out; end else begin count <= count + 1; end end endmodule ================================================ FILE: 06-modules-and-parameters/example-01-parameters/top-design.pcf ================================================ # Oscillator set_io clk 21 # LEDs set_io led[0] 99 set_io led[1] 98 # PMOD I/O set_io -pullup yes rst_btn 78 ================================================ FILE: 06-modules-and-parameters/example-01-parameters/top-design.v ================================================ // Example of a top-level design with 2 different clocks. // // Inputs: // clk - 12 MHz clock // rst_btn - pushbutton (RESET) // // Outputs: // led[1:0] - LEDs // // LED[0] should flash at 4 Hz and LED[1] should flash at 1 Hz. // // Date: November 8, 2021 // Author: Shawn Hymel // License: 0BSD // Top-level design that produces 2 different divided clocks module top_design ( // Inputs input clk, input rst_btn, // Outputs output [1:0] led // Not reg element! ); // Internal signals wire rst; // Invert active-low button assign rst = ~rst_btn; // Instantiate the first clock divider module clock_divider div_1 ( .clk(clk), .rst(rst), .out(led[0]) ); defparam div_1.COUNT_WIDTH = 32; defparam div_1.MAX_COUNT = 1500000 - 1; // Instantiate the second clock divider module clock_divider div_2 ( .clk(clk), .rst(rst), .out(led[1]) ); endmodule ================================================ FILE: 06-modules-and-parameters/example-02-ansi-parameters/apio.ini ================================================ [env] board = icestick ================================================ FILE: 06-modules-and-parameters/example-02-ansi-parameters/clock-divider.v ================================================ // Simple clock divider // // Parameters: // COUNT_WIDTH - Bus width for counter // MAX_COUNT - Maximum value of counter // // Inputs: // clk - Input clock // rst - Reset signal // // Outputs: // out - Divided clock output // // Toggles out line at a divided rate given clk input. // // Date: November 8, 2021 // Author: Shawn Hymel // License: 0BSD // Clock divider module clock_divider #( // Parameters parameter COUNT_WIDTH = 24, parameter [COUNT_WIDTH:0] MAX_COUNT = 6000000 - 1 ) ( // Inputs input clk, input rst, // Outputs output reg out ); // Internal signals wire rst; reg div_clk; reg [COUNT_WIDTH:0] count; // Clock divider always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin count <= 0; out <= 0; end else if (count == MAX_COUNT) begin count <= 0; out <= ~out; end else begin count <= count + 1; end end endmodule ================================================ FILE: 06-modules-and-parameters/example-02-ansi-parameters/top-design.pcf ================================================ # Oscillator set_io clk 21 # LEDs set_io led[0] 99 set_io led[1] 98 # PMOD I/O set_io -pullup yes rst_btn 78 ================================================ FILE: 06-modules-and-parameters/example-02-ansi-parameters/top-design.v ================================================ // Example of a top-level design with 2 different clocks. // // Inputs: // clk - 12 MHz clock // rst_btn - pushbutton (RESET) // // Outputs: // led[1:0] - LEDs // // LED[0] should flash at 4 Hz and LED[1] should flash at 1 Hz. // // Date: November 8, 2021 // Author: Shawn Hymel // License: 0BSD // Top-level design that produces 2 different divided clocks module top_design ( // Inputs input clk, input rst_btn, // Outputs output [1:0] led // Not reg element! ); // Internal signals wire rst; // Invert active-low button assign rst = ~rst_btn; // Instantiate the first clock divider module clock_divider #(.COUNT_WIDTH(32), .MAX_COUNT(1500000 - 1)) div_1 ( .clk(clk), .rst(rst), .out(led[0]) ); // Instantiate the second clock divider module clock_divider div_2 ( .clk(clk), .rst(rst), .out(led[1]) ); endmodule ================================================ FILE: 06-modules-and-parameters/solution-count-up-down/apio.ini ================================================ [env] board = icestick ================================================ FILE: 06-modules-and-parameters/solution-count-up-down/clock-divider.v ================================================ // Simple clock divider // // Parameters: // COUNT_WIDTH - Bus width for counter // MAX_COUNT - Maximum value of counter // // Inputs: // clk - Input clock // rst - Reset signal // // Outputs: // out - Divided clock output // // Toggles out line at a divided rate given clk input. // // Date: November 8, 2021 // Author: Shawn Hymel // License: 0BSD // Clock divider module clock_divider #( // Parameters parameter COUNT_WIDTH = 24, parameter [COUNT_WIDTH:0] MAX_COUNT = 6000000 - 1 ) ( // Inputs input clk, input rst, // Outputs output reg out ); // Internal signals wire rst; reg div_clk; reg [COUNT_WIDTH:0] count; // Clock divider always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin count <= 0; out <= 0; end else if (count == MAX_COUNT) begin count <= 0; out <= ~out; end else begin count <= count + 1; end end endmodule ================================================ FILE: 06-modules-and-parameters/solution-count-up-down/count-up-down-top.v ================================================ // One possible solution to the count up and down challenge // // Inputs: // clk - 12 MHz clock // rst_btn - pushbutton (RESET) // // Outputs: // led[3:0] - LEDs (connected to counter outputs) // // LEDs continuously count up and down. // // Date: November 9, 2021 // Author: Shawn Hymel // License: 0BSD // Top-level design that controls the clock divider and counter FSMs module count_up_down_top ( // Inputs input clk, input rst_btn, // Outputs output reg [3:0] led ); // Internal signals wire rst; wire div_clk; wire up_done; wire down_done; wire [3:0] up_out; wire [3:0] down_out; // Storage elements reg init_go; reg counting_up; // Invert active-low buttons assign rst = ~rst_btn; // Use the reset button to start the endless loop of counters always @ (posedge div_clk or posedge rst) begin if (rst == 1'b1) begin init_go <= 1'b1; end else begin init_go <= 1'b0; end end // Remember if we're counting up or down always @ (posedge div_clk or posedge rst) begin if (rst == 1'b1) begin counting_up <= 1'b1; end else begin if (up_done == 1'b1) begin counting_up <= 1'b0; end else if (down_done == 1'b1) begin counting_up <= 1'b1; end end end // Multiplexer to switch which counter is controlling the LEDs always @ ( * ) begin if (counting_up == 1'b1) begin led = up_out; end else begin led = down_out; end end // Clock divider clock_divider #(.COUNT_WIDTH(24), .MAX_COUNT(1500000 - 1)) div ( .clk(clk), .rst(rst), .out(div_clk) ); // Counter (up) counter_fsm #(.COUNT_UP(1), .MAX_COUNT(4'hF)) up_counter ( .clk(div_clk), .rst(rst), .go(down_done | init_go), .out(up_out), .done(up_done) ); // Counter (down) counter_fsm #(.COUNT_UP(0), .MAX_COUNT(4'hF)) down_counter ( .clk(div_clk), .rst(rst), .go(up_done), .out(down_out), .done(down_done) ); endmodule ================================================ FILE: 06-modules-and-parameters/solution-count-up-down/count-up-down.pcf ================================================ # Oscillator set_io clk 21 # LEDs set_io led[0] 99 set_io led[1] 98 set_io led[2] 97 set_io led[3] 96 # PMOD I/O set_io -pullup yes rst_btn 78 ================================================ FILE: 06-modules-and-parameters/solution-count-up-down/counter-fsm.v ================================================ // Count up on button signal and stop. // // Parameters: // COUNT_UP - 1 to count up, 0 to count down // MAX_COUNT - Maximum number to count to // // Inputs: // clk - Clock input to module // rst - Reset signal to clear state machine and counter // go - Go signal to start state machine // // Outputs: // out[3:0] - Bus that counts from 0x0 to 0xf or 0xf to 0x0 // done - Signal that pulses on 1 clock cycle when FSM is complete // // Send rising edge on go input to start counter. If up is 1, counter will // count up. Otherwise, it will count down. // // Date: November 9, 2021 // Author: Shawn Hymel // License: 0BSD // Mealy state machine that counts when go signal is sent module counter_fsm #( // Parameters parameter COUNT_UP = 1'b1, // 1'b1 to count up parameter MAX_COUNT = 4'hF // Maximum number to count to ) ( // Inputs input clk, input rst, input go, // Outputs output reg [3:0] out, output reg done ); // States localparam STATE_IDLE = 1'd0; localparam STATE_COUNTING = 1'd1; // Internal storage elements reg state; // State transition logic always @ (posedge clk or posedge rst) begin // On reset, return to idle state if (rst == 1'b1) begin state <= STATE_IDLE; // Define the state transitions end else begin case (state) // Wait for go signal STATE_IDLE: begin done <= 1'b0; if (go == 1'b1) begin state <= STATE_COUNTING; end end // Go from counting to done if counting reaches max STATE_COUNTING: begin if (COUNT_UP == 1'b1 && out == MAX_COUNT) begin done <= 1'b1; state <= STATE_IDLE; end else if (COUNT_UP == 1'b0 && out == 0) begin done <= 1'b1; state <= STATE_IDLE; end end // Go to idle if in unknown state default: state <= STATE_IDLE; endcase end end // Handle the counter output always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin out <= 4'd0; end else begin case (state) // Start counter at the right value STATE_IDLE: begin if (COUNT_UP == 1'b1) begin out <= 4'd0; end else begin out <= MAX_COUNT; end end // Count up or down STATE_COUNTING: begin if (COUNT_UP == 1'b1) begin if (out != MAX_COUNT) out <= out + 1; end else begin if (out != 4'h0) out <= out - 1; end end default: out <= out; endcase end end endmodule ================================================ FILE: 07-simulation-and-testbenches/example-01-testbench/apio.ini ================================================ [env] board = icestick ================================================ FILE: 07-simulation-and-testbenches/example-01-testbench/clk-div_tb.gtkw ================================================ [*] [*] GTKWave Analyzer v3.3.77 (w)1999-2016 BSI [*] Thu Nov 11 19:42:09 2021 [*] [dumpfile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\07-simulation-and-testbenches\example-01-testbench\clk-div_tb.vcd" [dumpfile_mtime] "Thu Nov 11 19:40:47 2021" [dumpfile_size] 4709 [savefile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\07-simulation-and-testbenches\example-01-testbench\clk-div_tb.gtkw" [timestart] 0 [size] 1000 600 [pos] -1 -1 *-22.000000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [treeopen] clock_divider_tb. [sst_width] 211 [signals_width] 110 [sst_expanded] 1 [sst_vpaned_height] 154 @28 clock_divider_tb.div_1.clk @22 clock_divider_tb.div_1.count[4:0] @28 clock_divider_tb.div_1.out clock_divider_tb.div_1.rst [pattern_trace] 1 [pattern_trace] 0 ================================================ FILE: 07-simulation-and-testbenches/example-01-testbench/clk-div_tb.v ================================================ // Testbench for clock divider // // Date: November 11, 2021 // Author: Shawn Hymel // License: 0BSD // Defines timescale for simulation: / `timescale 1 ns / 10 ps // Define our testbench module clock_divider_tb(); // Internal signals wire out; // Storage elements (set initial values to 0) reg clk = 0; reg rst = 0; // Simulation time: 10000 * 1 ns = 10 us localparam DURATION = 10000; // Generate clock signal: 1 / ((2 * 41.67) * 1 ns) = 11,999,040.08 MHz always begin // Delay for 41.67 time units // 10 ps precision means that 41.667 is rounded to 41.67 #41.667 // Toggle clock line clk = ~clk; end // Instantiate the unit under test (UUT) clock_divider #(.COUNT_WIDTH(4), .MAX_COUNT(6 - 1)) uut ( .clk(clk), .rst(rst), .out(out) ); // Pulse reset line high at the beginning initial begin #10 rst = 1'b1; #1 rst = 1'b0; end // Run simulation (output to .vcd file) initial begin // Create simulation output file $dumpfile("clk-div_tb.vcd"); $dumpvars(0, clock_divider_tb); // Wait for given amount of time for simulation to complete #(DURATION) // Notify and end simulation $display("Finished!"); $finish; end endmodule ================================================ FILE: 07-simulation-and-testbenches/example-01-testbench/clock-divider.v ================================================ // Simple clock divider // // Parameters: // COUNT_WIDTH - Bus width for counter // MAX_COUNT - Maximum value of counter // // Inputs: // clk - Input clock // rst - Reset signal // // Outputs: // out - Divided clock output // // Toggles out line at a divided rate given clk input. // // Date: November 8, 2021 // Author: Shawn Hymel // License: 0BSD // Clock divider module clock_divider ( // Inputs input clk, input rst, // Outputs output reg out ); // Parameters parameter COUNT_WIDTH = 24; parameter [COUNT_WIDTH-1:0] MAX_COUNT = 6000000 - 1; // Internal signals wire rst; reg div_clk; reg [COUNT_WIDTH:0] count; // Clock divider always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin count <= 0; out <= 0; end else if (count == MAX_COUNT) begin count <= 0; out <= ~out; end else begin count <= count + 1; end end endmodule ================================================ FILE: 07-simulation-and-testbenches/solution-debounce-testbench/apio.ini ================================================ [env] board = icestick ================================================ FILE: 07-simulation-and-testbenches/solution-debounce-testbench/button-debouncing.v ================================================ // One possible way to debounce a button press (using a Moore state machine) // // Parameters: // MAX_CLK_COUNT - How long to wait (cycles) before sampling button again // // Inputs: // clk - Input clock // rst_btn - Pushbutton (RESET) // inc_btn - Pushbutton (INCREMENT) // // Outputs: // led[3:0] - LEDs (count from 0x0 to 0xf) // // One press of the increment button should correspond to one and only one // increment of the counter. Use a state machine to identify the edge on the // button line, wait 40 ms, and sample again to see if the button is still // pressed. // // Date: November 11, 2021 // Author: Shawn Hymel // License: 0BSD // Use a state machine to debounce the button, which increments a counter module debounced_counter #( // Parameters parameter MAX_CLK_COUNT = 20'd480000 - 1 ) ( // Inputs input clk, input rst_btn, input inc_btn, // Outputs output reg [3:0] led ); // States localparam STATE_HIGH = 2'd0; localparam STATE_LOW = 2'd1; localparam STATE_WAIT = 2'd2; localparam STATE_PRESSED = 2'd3; // Internal signals wire rst; wire inc; // Internal storage elements reg [1:0] state; reg [19:0] clk_count; // Invert active-low buttons assign rst = ~rst_btn; assign inc = ~inc_btn; // State transition logic always @ (posedge clk or posedge rst) begin // On reset, return to idle state and restart counters if (rst == 1'b1) begin state <= STATE_HIGH; led <= 4'd0; // Define the state transitions end else begin case (state) // Wait for increment signal to go from high to low STATE_HIGH: begin if (inc == 1'b0) begin state <= STATE_LOW; end end // Wait for increment signal to go from low to high STATE_LOW: begin if (inc == 1'b1) begin state <= STATE_WAIT; end end // Wait for count to be done and sample button again STATE_WAIT: begin if (clk_count == MAX_CLK_COUNT) begin if (inc == 1'b1) begin state <= STATE_PRESSED; end else begin state <= STATE_HIGH; end end end // If button is still pressed, increment LED counter STATE_PRESSED: begin led <= led + 1; state <= STATE_HIGH; end // Default case: return to idle state default: state <= STATE_HIGH; endcase end end // Run counter if in wait state always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin clk_count <= 0; end else begin if (state == STATE_WAIT) begin clk_count <= clk_count + 1; end else begin clk_count <= 0; end end end endmodule ================================================ FILE: 07-simulation-and-testbenches/solution-debounce-testbench/button-debouncing_tb.gtkw ================================================ [*] [*] GTKWave Analyzer v3.3.77 (w)1999-2016 BSI [*] Thu Nov 11 22:58:44 2021 [*] [dumpfile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\07-simulation-and-testbenches\solution-debounce-testbench\button-debouncing_tb.vcd" [dumpfile_mtime] "Thu Nov 11 22:56:49 2021" [dumpfile_size] 4760913 [savefile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\07-simulation-and-testbenches\solution-debounce-testbench\button-debouncing_tb.gtkw" [timestart] 0 [size] 1000 600 [pos] -1 -1 *-31.000000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [treeopen] button_debouncing_tb. [sst_width] 211 [signals_width] 110 [sst_expanded] 1 [sst_vpaned_height] 154 @28 button_debouncing_tb.uut.clk button_debouncing_tb.uut.inc_btn @29 button_debouncing_tb.uut.state[1:0] @22 button_debouncing_tb.uut.led[3:0] [pattern_trace] 1 [pattern_trace] 0 ================================================ FILE: 07-simulation-and-testbenches/solution-debounce-testbench/button-debouncing_tb.v ================================================ // Testbench for button debounce design // // Date: November 11, 2021 // Author: Shawn Hymel // License: 0BSD // Define timescale `timescale 1 us / 10 ps // Define our testbench module button_debouncing_tb(); // Internal signals wire [3:0] out; // Storage elements (buttons are active low!) reg clk = 0; reg rst_btn = 1; reg inc_btn = 1; integer i; // Used in for loop integer j; // Used in for loop integer prev_inc; // Previous increment button state integer nbounces; // Holds random number integer rdelay; // Holds random number // Simulation time: 10000 * 1 us = 10 ms localparam DURATION = 10000; // Generate clock signal (about 12 MHz) always begin #0.04167 clk = ~clk; end // Instantiate debounce/counter module (use about 400 us wait time) debounced_counter #(.MAX_CLK_COUNT(4800 - 1)) uut ( .clk(clk), .rst_btn(rst_btn), .inc_btn(inc_btn), .led(out) ); // Test control: pulse reset and create some (bouncing) button presses initial begin // Pulse reset low to reset state machine #10 rst_btn = 0; #1 rst_btn = 1; // We can use for loops in simulation! for (i = 0; i < 32; i = i + 1) begin // Wait some time before pressing button #1000 // Simulate a bouncy/noisy button press // $urandom generates a 32-bit unsigned (pseudo) random number // "% 10" is "modulo 10" prev_inc = inc_btn; nbounces = $urandom % 20; for (j = 0; j < nbounces; j = j + 1) begin #($urandom % 10) inc_btn = ~inc_btn; end // Make sure button ends up in the opposite state inc_btn = ~prev_inc; end end // Run simulation (output to .vcd file) initial begin // Create simulation output file $dumpfile("button-debouncing_tb.vcd"); $dumpvars(0, button_debouncing_tb); // Wait for given amount of time for simulation to complete #(DURATION) // Notify and end simulation $display("Finished!"); $finish; end endmodule ================================================ FILE: 08-memory/example-01-memory/apio.ini ================================================ [env] board = icestick ================================================ FILE: 08-memory/example-01-memory/memory.v ================================================ // Simple block RAM example // // Inputs: // clk - Input clock // w_en - Write enable // r_en - Read enable // w_addr[3:0] - Write address // r_addr[3:0] - Read address // w_data[7:0] - Data to be written // // Outputs: // r_data[7:0] - Data to be read // // Date: November 15, 2021 // Author: Shawn Hymel // License: 0BSD // Inferred block RAM module memory ( // Inputs input clk, input w_en, input r_en, input [3:0] w_addr, input [3:0] r_addr, input [7:0] w_data, // Outputs output reg [7:0] r_data ); // Declare memory reg [7:0] mem [0:15]; // Interact with the memory block always @ (posedge clk) begin // Write to memory if (w_en == 1'b1) begin mem[w_addr] <= w_data; end // Read from memory if (r_en == 1'b1) begin r_data <= mem[r_addr]; end end endmodule ================================================ FILE: 08-memory/example-01-memory/memory_tb.gtkw ================================================ [*] [*] GTKWave Analyzer v3.3.77 (w)1999-2016 BSI [*] Mon Nov 15 20:31:41 2021 [*] [dumpfile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\08-memory\example-01-memory\memory_tb.vcd" [dumpfile_mtime] "Mon Nov 15 20:31:26 2021" [dumpfile_size] 4133 [savefile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\08-memory\example-01-memory\memory_tb.gtkw" [timestart] 0 [size] 1000 600 [pos] -1 -1 *-19.000000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [sst_width] 211 [signals_width] 118 [sst_expanded] 1 [sst_vpaned_height] 154 @28 memory_tb.clk @22 memory_tb.r_addr[3:0] memory_tb.r_data[7:0] @28 memory_tb.r_en @22 memory_tb.w_addr[3:0] memory_tb.w_data[7:0] @28 memory_tb.w_en [pattern_trace] 1 [pattern_trace] 0 ================================================ FILE: 08-memory/example-01-memory/memory_tb.v ================================================ // Testbench for memory // // First, read from address in memory (should be garbage or unknown). Next, // write to that location and read from it again. Output should match input. // // Date: November 15, 2021 // Author: Shawn Hymel // License: 0BSD // Defines timescale for simulation: / `timescale 1 ns / 10 ps // Define our testbench module memory_tb(); // Internal signals wire [7:0] r_data; // Storage elements (set initial values to 0) reg clk = 0; reg w_en = 0; reg r_en = 0; reg [3:0] w_addr; reg [3:0] r_addr; reg [7:0] w_data; // Simulation time: 10000 * 1 ns = 10 us localparam DURATION = 10000; // Generate clock signal: 1 / ((2 * 41.67) * 1 ns) = 11,999,040.08 MHz always begin #41.67 clk = ~clk; end // Instantiate the unit under test (UUT) memory uut ( .clk(clk), .w_en(w_en), .r_en(r_en), .w_addr(w_addr), .r_addr(r_addr), .w_data(w_data), .r_data(r_data) ); // Run test: write to location and read value back initial begin // Test 1: read from address 0x10 (should be garbage) #(2 * 41.67) r_addr = 'h0f; r_en = 1; #(2 * 41.67) r_addr = 0; r_en = 0; // Test 2: Write to address 0x0f and read it back #(2 * 41.67) w_addr = 'h0f; w_data = 'hA5; w_en = 1; #(2 * 41.67) w_addr = 0; w_data = 0; w_en = 0; r_addr = 'h0f; r_en = 1; #(2 * 41.67) r_addr = 0; r_en = 0; end // Run simulation (output to .vcd file) initial begin // Create simulation output file $dumpfile("memory_tb.vcd"); $dumpvars(0, memory_tb); // Wait for given amount of time for simulation to complete #(DURATION) // Notify and end simulation $display("Finished!"); $finish; end endmodule ================================================ FILE: 08-memory/example-01-memory/test.pcf ================================================ # Oscillator set_io clk 21 # Dummy ports set_io w_en 44 set_io r_en 45 set_io w_addr[0] 78 set_io w_addr[1] 79 set_io w_addr[2] 80 set_io w_addr[3] 81 set_io r_addr[0] 87 set_io r_addr[1] 88 set_io r_addr[2] 90 set_io r_addr[3] 91 set_io w_data[0] 112 set_io w_data[1] 113 set_io w_data[2] 114 set_io w_data[3] 115 set_io w_data[4] 116 set_io w_data[5] 117 set_io w_data[6] 118 set_io w_data[7] 119 set_io r_data[0] 95 set_io r_data[1] 96 set_io r_data[2] 97 set_io r_data[3] 98 set_io r_data[4] 99 set_io r_data[5] 60 set_io r_data[6] 61 set_io r_data[7] 62 ================================================ FILE: 08-memory/example-02-memory-initialization/apio.ini ================================================ [env] board = icestick ================================================ FILE: 08-memory/example-02-memory-initialization/mem_init.txt ================================================ 00 01 02 03 04 05 06 07 b8 b9 ba bb bc bd be bf ================================================ FILE: 08-memory/example-02-memory-initialization/memory.v ================================================ // Simple block RAM example // // Inputs: // clk - Input clock // w_en - Write enable // r_en - Read enable // w_addr[3:0] - Write address // r_addr[3:0] - Read address // w_data[7:0] - Data to be written // // Outputs: // r_data[7:0] - Data to be read // // Date: November 15, 2021 // Author: Shawn Hymel // License: 0BSD // Inferred block RAM module memory #( // Parameters parameter INIT_FILE = "" ) ( // Inputs input clk, input w_en, input r_en, input [3:0] w_addr, input [3:0] r_addr, input [7:0] w_data, // Outputs output reg [7:0] r_data ); // Declare memory reg [7:0] mem [0:15]; // Interact with the memory block always @ (posedge clk) begin // Write to memory if (w_en == 1'b1) begin mem[w_addr] <= w_data; end // Read from memory if (r_en == 1'b1) begin r_data <= mem[r_addr]; end end // Initialization (if available) initial if (INIT_FILE) begin $readmemh(INIT_FILE, mem); end endmodule ================================================ FILE: 08-memory/example-02-memory-initialization/memory_tb.gtkw ================================================ [*] [*] GTKWave Analyzer v3.3.77 (w)1999-2016 BSI [*] Mon Nov 15 20:27:07 2021 [*] [dumpfile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\08-memory\example-02-memory-initialization\memory_tb.vcd" [dumpfile_mtime] "Mon Nov 15 20:25:26 2021" [dumpfile_size] 5178 [savefile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\08-memory\example-02-memory-initialization\memory_tb.gtkw" [timestart] 0 [size] 1000 600 [pos] 1230 252 *-20.000000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [sst_width] 211 [signals_width] 118 [sst_expanded] 1 [sst_vpaned_height] 154 @28 memory_tb.clk @420 memory_tb.i @22 memory_tb.r_addr[3:0] memory_tb.r_data[7:0] @28 memory_tb.r_en @22 memory_tb.w_addr[3:0] memory_tb.w_data[7:0] @28 memory_tb.w_en [pattern_trace] 1 [pattern_trace] 0 ================================================ FILE: 08-memory/example-02-memory-initialization/memory_tb.v ================================================ // Testbench for memory // // First, read from address in memory (should be garbage or unknown). Next, // write to that location and read from it again. Output should match input. // // Date: November 15, 2021 // Author: Shawn Hymel // License: 0BSD // Defines timescale for simulation: / `timescale 1 ns / 10 ps // Define our testbench module memory_tb(); // Internal signals wire [7:0] r_data; // Storage elements (set initial values to 0) reg clk = 0; reg w_en = 0; reg r_en = 0; reg [3:0] w_addr; reg [3:0] r_addr; reg [7:0] w_data; integer i; // Simulation time: 10000 * 1 ns = 10 us localparam DURATION = 10000; // Generate clock signal: 1 / ((2 * 41.67) * 1 ns) = 11,999,040.08 MHz always begin #41.67 clk = ~clk; end // Instantiate the unit under test (UUT) memory #(.INIT_FILE("mem_init.txt")) uut ( .clk(clk), .w_en(w_en), .r_en(r_en), .w_addr(w_addr), .r_addr(r_addr), .w_data(w_data), .r_data(r_data) ); // Run test: write to location and read value back initial begin // Test 1: read initial values for (i = 0; i < 16; i = i + 1) begin #(2 * 41.67) r_addr = i; r_en = 1; #(2 * 41.67) r_addr = 0; r_en = 0; end // Test 2: Write to address 0x0f and read it back #(2 * 41.67) w_addr = 'h0f; w_data = 'hA5; w_en = 1; #(2 * 41.67) w_addr = 0; w_data = 0; w_en = 0; r_addr = 'h0f; r_en = 1; #(2 * 41.67) r_addr = 0; r_en = 0; // Test 3: Read and write at same time #(2 * 41.67) w_addr = 'h0a; w_data = 'hef; w_en = 1; r_addr = 'h0a; r_en = 1; #(2 * 41.67) w_addr = 0; w_data = 0; w_en = 0; r_addr = 0; r_en = 0; end // Run simulation (output to .vcd file) initial begin // Create simulation output file $dumpfile("memory_tb.vcd"); $dumpvars(0, memory_tb); // Wait for given amount of time for simulation to complete #(DURATION) // Notify and end simulation $display("Finished!"); $finish; end endmodule ================================================ FILE: 08-memory/example-02-memory-initialization/test.pcf ================================================ # Oscillator set_io clk 21 # Dummy ports set_io w_en 44 set_io r_en 45 set_io w_addr[0] 78 set_io w_addr[1] 79 set_io w_addr[2] 80 set_io w_addr[3] 81 set_io r_addr[0] 87 set_io r_addr[1] 88 set_io r_addr[2] 90 set_io r_addr[3] 91 set_io w_data[0] 112 set_io w_data[1] 113 set_io w_data[2] 114 set_io w_data[3] 115 set_io w_data[4] 116 set_io w_data[5] 117 set_io w_data[6] 118 set_io w_data[7] 119 set_io r_data[0] 95 set_io r_data[1] 96 set_io r_data[2] 97 set_io r_data[3] 98 set_io r_data[4] 99 set_io r_data[5] 60 set_io r_data[6] 61 set_io r_data[7] 62 ================================================ FILE: 08-memory/solution-sequencer/apio.ini ================================================ [env] board = icestick ================================================ FILE: 08-memory/solution-sequencer/clock-divider.v ================================================ // Simple clock divider // // Parameters: // COUNT_WIDTH - Bus width for counter // MAX_COUNT - Maximum value of counter // // Inputs: // clk - Input clock // rst - Reset signal // // Outputs: // out - Divided clock output // // Toggles out line at a divided rate given clk input. // // Date: November 8, 2021 // Author: Shawn Hymel // License: 0BSD // Clock divider module clock_divider #( // Parameters parameter COUNT_WIDTH = 24, parameter [COUNT_WIDTH:0] MAX_COUNT = 6000000 - 1 ) ( // Inputs input clk, input rst, // Outputs output reg out ); // Internal signals wire rst; reg div_clk; reg [COUNT_WIDTH:0] count; // Clock divider always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin count <= 0; out <= 0; end else if (count == MAX_COUNT) begin count <= 0; out <= ~out; end else begin count <= count + 1; end end endmodule ================================================ FILE: 08-memory/solution-sequencer/debouncer.v ================================================ // One possible way to debounce a button press (using a Moore state machine) // // Inputs: // clk - 12 MHz clock // rst - reset signal // in - input signal // // Outputs: // out - output signal (debounced) // // Single pulse sent on out when in signal goes high for some time. // // Date: November 15, 2021 // Author: Shawn Hymel // License: 0BSD // Use a state machine to debounce a button module debouncer #( // Max counts for wait state (40 ms with 12 MHz clock) parameter COUNT_WIDTH = 20, parameter [COUNT_WIDTH:0] MAX_CLK_COUNT = 480000 - 1 ) ( // Inputs input clk, input rst, input in, // Outputs output reg out ); // States localparam STATE_HIGH = 2'd0; localparam STATE_LOW = 2'd1; localparam STATE_WAIT = 2'd2; localparam STATE_PRESSED = 2'd3; // Internal storage elements reg [1:0] state; reg [COUNT_WIDTH:0] clk_count; // State transition logic always @ (posedge clk or posedge rst) begin // On reset, return to idle state if (rst == 1'b1) begin state <= STATE_HIGH; out <= 1'b0; // Define the state transitions end else begin case (state) // Wait for increment signal to go from high to low STATE_HIGH: begin out <= 1'b0; if (in == 1'b0) begin state <= STATE_LOW; end end // Wait for increment signal to go from low to high STATE_LOW: begin if (in == 1'b1) begin state <= STATE_WAIT; end end // Wait for count to be done and sample button again STATE_WAIT: begin if (clk_count == MAX_CLK_COUNT) begin if (in == 1'b1) begin state <= STATE_PRESSED; end else begin state <= STATE_HIGH; end end end // If button is still pressed STATE_PRESSED: begin out <= 1'b1; state <= STATE_HIGH; end // Default case: return to idle state default: state <= STATE_HIGH; endcase end end // Run counter if in wait state always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin clk_count <= 0; end else begin if (state == STATE_WAIT) begin clk_count <= clk_count + 1; end else begin clk_count <= 0; end end end endmodule ================================================ FILE: 08-memory/solution-sequencer/mem_init.txt ================================================ 0 0 0 0 1 1 1 1 ================================================ FILE: 08-memory/solution-sequencer/memory.v ================================================ // Simple block RAM example // // Inputs: // clk - Input clock // w_en - Write enable // r_en - Read enable // w_addr[x:0] - Write address // r_addr[x:0] - Read address // w_data[x:0] - Data to be written // // Outputs: // r_data[x:0] - Data to be read // // Date: November 15, 2021 // Author: Shawn Hymel // License: 0BSD // Inferred block RAM module memory #( // Parameters parameter MEM_WIDTH = 16, parameter MEM_DEPTH = 256, parameter INIT_FILE = "" ) ( // Inputs input clk, input w_en, input r_en, input [ADDR_WIDTH - 1:0] w_addr, input [ADDR_WIDTH - 1:0] r_addr, input [MEM_WIDTH - 1:0] w_data, // Outputs output reg [MEM_WIDTH - 1:0] r_data ); // Calculate the number of bits required for the address localparam ADDR_WIDTH = $clog2(MEM_DEPTH); // Declare memory reg [MEM_WIDTH - 1:0] mem [0:MEM_DEPTH - 1]; // Interact with the memory block always @ (posedge clk) begin // Write to memory if (w_en) begin mem[w_addr] <= w_data; end // Read from memory if (r_en) begin r_data <= mem[r_addr]; end end // Initialization (if available) initial if (INIT_FILE) begin $readmemh(INIT_FILE, mem); end endmodule ================================================ FILE: 08-memory/solution-sequencer/sequencer-top.pcf ================================================ # Oscillator set_io clk 21 # LEDs set_io led[0] 99 set_io led[1] 98 set_io unused_led[0] 97 set_io unused_led[1] 96 set_io unused_led[2] 95 # PMOD I/O set_io -pullup yes rst_btn 78 set_io -pullup yes set_btn 79 set_io -pullup yes ptn_0_btn 80 set_io -pullup yes ptn_1_btn 81 ================================================ FILE: 08-memory/solution-sequencer/sequencer-top.v ================================================ // Top-level design for sequencer // // Inputs: // clk - 12 MHz clock // rst_btn - pushbutton (RESET) // set_btn - pushbutton (SET value at next memory address) // ptn_0_btn - pushbutton (PATTERN[0] for sequence) // ptn_1_btn - pushbutton (PATTERN[1] for sequence) // // Outputs: // led[1:0] - LEDs // // Push RESET to set everything to 0. Hold PATTERN[0] and/or PATTERN[1] and // press SET to record value in memory. Continue doing this for up to 8 memory // locations. Sequence will play on LEDs and loop forever. // // Date: November 15, 2021 // Author: Shawn Hymel // License: 0BSD // Top-level design for the sequencer module sequencer_top #( // Parameters parameter DEBOUNCE_COUNTS = 480000 - 1, // Counts for debounce wait parameter STEP_COUNTS = 6000000 - 1, // Clock cycles between steps parameter NUM_STEPS = 8 // Number of steps ) ( // Inputs input clk, input rst_btn, input set_btn, input ptn_0_btn, input ptn_1_btn, // Outputs output reg [1:0] led, output [2:0] unused_led ); // Internal signals wire rst; wire set; wire set_d; wire [1:0] ptn; wire div_clk; wire [1:0] r_data; // Storage elements (initialize some values) reg w_en = 0; reg r_en = 1'b1; // Always high! reg [2:0] w_addr = 0; reg [2:0] r_addr; reg [1:0] w_data; reg [2:0] step_counter; reg [2:0] mem_ptr = 0; // Turn off unused LEDs assign unused_led = 3'b000; // Invert active-low buttons assign rst = ~rst_btn; assign set = ~set_btn; assign ptn[0] = ~ptn_0_btn; assign ptn[1] = ~ptn_1_btn; // Clock divider clock_divider #( .COUNT_WIDTH(24), .MAX_COUNT(STEP_COUNTS) ) div ( .clk(clk), .rst(rst), .out(div_clk) ); // Button debouncer for set buttons debouncer #( .COUNT_WIDTH(24), .MAX_CLK_COUNT(DEBOUNCE_COUNTS) ) set_debouncer ( .clk(clk), .rst(rst), .in(set), .out(set_d) ); // Memory unit memory #( .MEM_WIDTH(2), .MEM_DEPTH(NUM_STEPS), .INIT_FILE("mem_init.txt") ) mem ( .clk(clk), .w_en(w_en), .r_en(r_en), .w_addr(w_addr), .r_addr(r_addr), .w_data(w_data), .r_data(r_data) ); // Read from memory each divided clock cycle always @ (posedge div_clk or posedge rst) begin if (rst == 1'b1) begin led <= 0; r_addr <= 0; step_counter <= 0; end else begin r_addr <= step_counter; step_counter <= step_counter + 1; led <= r_data; end end // Register write data as soon as debounced set signal goes high always @ (posedge set_d) begin w_data <= ptn; end // Handle writing pattern to memory always @ (posedge clk or posedge rst) begin // Reset memory address pointer and write enable signal if (rst == 1'b1) begin mem_ptr <= 0; w_en <= 1'b0; // Set write enable high and increment memory pointer end else if (set_d == 1'b1) begin w_addr <= mem_ptr; w_en <= 1'b1; mem_ptr <= mem_ptr + 1; // Reset write enable signal end else begin w_en <= 1'b0; end end endmodule ================================================ FILE: 08-memory/solution-sequencer/sequencer-top_tb.gtkw ================================================ [*] [*] GTKWave Analyzer v3.3.77 (w)1999-2016 BSI [*] Tue Nov 16 17:21:28 2021 [*] [dumpfile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\08-memory\solution-sequencer\sequencer-top_tb.vcd" [dumpfile_mtime] "Tue Nov 16 17:20:28 2021" [dumpfile_size] 25572 [savefile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\08-memory\solution-sequencer\sequencer-top_tb.gtkw" [timestart] 0 [size] 1000 600 [pos] 143 193 *-21.000000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [treeopen] memory_tb. [treeopen] memory_tb.uut. [sst_width] 211 [signals_width] 118 [sst_expanded] 1 [sst_vpaned_height] 154 @28 memory_tb.clk @29 memory_tb.uut.div_clk @28 memory_tb.led[1:0] memory_tb.ptn_0_btn memory_tb.ptn_1_btn memory_tb.rst_btn memory_tb.set_btn memory_tb.uut.mem.r_addr[2:0] memory_tb.uut.mem.r_data[1:0] memory_tb.uut.mem.r_en memory_tb.uut.mem.w_addr[2:0] memory_tb.uut.mem.w_data[1:0] memory_tb.uut.mem.w_en [pattern_trace] 1 [pattern_trace] 0 ================================================ FILE: 08-memory/solution-sequencer/sequencer-top_tb.v ================================================ // Testbench for sequencer // // Make sure the sequencer can store and replay step values. // // Date: November 15, 2021 // Author: Shawn Hymel // License: 0BSD // Defines timescale for simulation: / `timescale 1 ns / 10 ps // Define our testbench module memory_tb(); // Internal signals wire [1:0] led; // Storage elements reg clk = 0; reg rst_btn = 1; reg set_btn = 1; reg ptn_0_btn = 1; reg ptn_1_btn = 1; // Simulation time: 50000 * 1 ns = 50 us localparam DURATION = 50000; // Generate clock signal: 1 / ((2 * 41.67) * 1 ns) = 11,999,040.08 MHz always begin #41.67 clk = ~clk; end // Instantiate the unit under test (UUT) sequencer_top #( .DEBOUNCE_COUNTS(2), .STEP_COUNTS(10), .NUM_STEPS(8) ) uut ( .clk(clk), .rst_btn(rst_btn), .set_btn(set_btn), .ptn_0_btn(ptn_0_btn), .ptn_1_btn(ptn_1_btn), .led(led), .unused_led() ); // Run test: write to sequencer and make sure it plays the sequence back initial begin // Toggle reset #10 rst_btn = 0; #1 rst_btn = 1; // View a few default steps #(50 * 41.67) // Write 'b11 to the sequencer #(10 * 41.67) ptn_0_btn = 0; ptn_1_btn = 0; set_btn = 0; #(10 * 41.67) ptn_0_btn = 1; ptn_1_btn = 1; set_btn = 1; // Write 'b11 to the sequencer #(10 * 41.67) ptn_0_btn = 0; ptn_1_btn = 0; set_btn = 0; #(10 * 41.67) ptn_0_btn = 1; ptn_1_btn = 1; set_btn = 1; end // Run simulation (output to .vcd file) initial begin // Create simulation output file $dumpfile("sequencer-top_tb.vcd"); $dumpvars(0, memory_tb); // Wait for given amount of time for simulation to complete #(DURATION) // Notify and end simulation $display("Finished!"); $finish; end endmodule ================================================ FILE: 09-pll-and-glitches/example-01-pll/apio.ini ================================================ [env] board = icestick ================================================ FILE: 09-pll-and-glitches/example-01-pll/pll_test.pcf ================================================ # Oscillator set_io ref_clk 21 # PMOD I/O set_io clk 87 ================================================ FILE: 09-pll-and-glitches/example-01-pll/pll_test.v ================================================ // Simple block RAM example // // Inputs: // ref_clk - Input reference clock (12 MHz) // // Outputs: // clk - Output of PLL (120 MHz clock) // // Date: December 6, 2021 // Author: Shawn Hymel // License: 0BSD // Instantiate PLL and output clock signal to pin module pll_test ( // Inputs input ref_clk, // Outputs output clk ); // Instantiate PLL (120 MHz) SB_PLL40_CORE #( .FEEDBACK_PATH("SIMPLE"), // Don't use fine delay adjust .PLLOUT_SELECT("GENCLK"), // No phase shift on output .DIVR(4'b0000), // Reference clock divider .DIVF(7'b1001111), // Feedback clock divider .DIVQ(3'b011), // VCO clock divider .FILTER_RANGE(3'b001) // Filter range ) pll ( .REFERENCECLK(ref_clk), // Input clock .PLLOUTCORE(clk), // Output clock .LOCK(), // Locked signal .RESETB(1'b1), // Active low reset .BYPASS(1'b0) // No bypass, use PLL signal as output ); endmodule ================================================ FILE: 09-pll-and-glitches/example-02-glitches/apio.ini ================================================ [env] board = icestick ================================================ FILE: 09-pll-and-glitches/example-02-glitches/empty.v ================================================ ================================================ FILE: 09-pll-and-glitches/example-02-glitches/glitch-test_tb.gtkw ================================================ [*] [*] GTKWave Analyzer v3.3.77 (w)1999-2016 BSI [*] Mon Dec 06 17:26:08 2021 [*] [dumpfile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\09-pll-and-glitches\example-02-glitches\glitch-test_tb.vcd" [dumpfile_mtime] "Mon Dec 06 17:25:24 2021" [dumpfile_size] 153010 [savefile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\09-pll-and-glitches\example-02-glitches\glitch-test_tb.gtkw" [timestart] 0 [size] 1000 600 [pos] -1 -1 *-15.000000 12501 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [sst_width] 211 [signals_width] 110 [sst_expanded] 1 [sst_vpaned_height] 154 @28 glitch_test_tb.rst glitch_test_tb.clk @c00023 glitch_test_tb.out[3:0] @28 (0)glitch_test_tb.out[3:0] (1)glitch_test_tb.out[3:0] (2)glitch_test_tb.out[3:0] (3)glitch_test_tb.out[3:0] @1401201 -group_end @22 glitch_test_tb.count[3:0] [pattern_trace] 1 [pattern_trace] 0 ================================================ FILE: 09-pll-and-glitches/example-02-glitches/glitch-test_tb.v ================================================ // Simulation of glitches with a ripple-carry adder. // // Date: December 6, 2021 // Author: Shawn Hymel // License: 0BSD // Defines timescale for simulation: / `timescale 1 ns / 1 ps // Half adder (with gate delays) module half_adder ( // Inputs input a, input b, // Output output sum, output c_out ); // Create output logic assign #1 sum = a ^ b; assign #1 c_out = a & b; endmodule // Define our testbench module glitch_test_tb(); // Simulation time: 10000 * 1 ns = 10000 ns localparam DURATION = 10000; // Internal signals wire c_0; wire c_1; wire c_2; wire [3:0] out; // Internal storage elements reg clk = 0; reg rst = 0; reg [3:0] count; // Generate clock signal: 1 / ((2 * 4.167) * 1 ns) = 119,990,400.77 Hz always begin #4.167 clk = ~clk; end // Instantiate half adders half_adder ha_0 (.a(1'b1), .b(count[0]), .sum(out[0]), .c_out(c_0)); half_adder ha_1 (.a(c_0), .b(count[1]), .sum(out[1]), .c_out(c_1)); half_adder ha_2 (.a(c_1), .b(count[2]), .sum(out[2]), .c_out(c_2)); half_adder ha_3 (.a(c_2), .b(count[3]), .sum(out[3]), .c_out()); // Let the counter count always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin count = 0; end else begin count <= out; end end // Pulse reset line high at the beginning initial begin #10 rst = 1; #1 rst = 0; end // Run simulation (output to .vcd file) initial begin // Create simulation output file $dumpfile("glitch-test_tb.vcd"); $dumpvars(0, glitch_test_tb); // Wait for given amount of time for simulation to complete #(DURATION) // Notify and end simulation $display("Finished!"); $finish; end endmodule ================================================ FILE: 09-pll-and-glitches/solution-gray-code-counter/apio.ini ================================================ [env] board = icestick ================================================ FILE: 09-pll-and-glitches/solution-gray-code-counter/empty.v ================================================ // This file is needed to make apio happy ================================================ FILE: 09-pll-and-glitches/solution-gray-code-counter/gray-code-counter_tb.gtkw ================================================ [*] [*] GTKWave Analyzer v3.3.77 (w)1999-2016 BSI [*] Mon Dec 06 17:23:51 2021 [*] [dumpfile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\09-pll-and-glitches\solution-gray-code-counter\gray-code-counter_tb.vcd" [dumpfile_mtime] "Mon Dec 06 17:23:21 2021" [dumpfile_size] 65129 [savefile] "C:\Users\sgmustadio\Documents\GitHub\introduction-to-fpga\09-pll-and-glitches\solution-gray-code-counter\gray-code-counter_tb.gtkw" [timestart] 0 [size] 1000 600 [pos] -1 -1 *-15.000000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [sst_width] 211 [signals_width] 94 [sst_expanded] 1 [sst_vpaned_height] 154 @29 glitch_test_tb.rst @28 glitch_test_tb.clk @22 glitch_test_tb.out[3:0] [pattern_trace] 1 [pattern_trace] 0 ================================================ FILE: 09-pll-and-glitches/solution-gray-code-counter/gray-code-counter_tb.v ================================================ // Simulation of a 4-bit gray code counter using a finite state machine. // // Date: December 6, 2021 // Author: Shawn Hymel // License: 0BSD // Defines timescale for simulation: / `timescale 1 ns / 1 ps // 4-bit gray code counter FSM module gray_counter ( // Inputs input clk, input rst, // Outputs output reg [3:0] count ); // State machine transitions (with simulated delays) always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin count <= 0; end else begin case (count) // Gray code state 4'b0000: #1 count <= 4'b0001; // 0 4'b0001: #1 count <= 4'b0011; // 1 4'b0011: #1 count <= 4'b0010; // 2 4'b0010: #1 count <= 4'b0110; // 3 4'b0110: #1 count <= 4'b0111; // 4 4'b0111: #1 count <= 4'b0101; // 5 4'b0101: #1 count <= 4'b0100; // 6 4'b0100: #1 count <= 4'b1100; // 7 4'b1100: #1 count <= 4'b1101; // 8 4'b1101: #1 count <= 4'b1111; // 9 4'b1111: #1 count <= 4'b1110; // 10 4'b1110: #1 count <= 4'b1010; // 11 4'b1010: #1 count <= 4'b1011; // 12 4'b1011: #1 count <= 4'b1001; // 13 4'b1001: #1 count <= 4'b1000; // 14 4'b1000: #1 count <= 4'b0000; // 15 default: #1 count <= 4'b0000; endcase end end endmodule // Define our testbench module gray_counter_tb(); // Simulation time: 10000 * 1 ns = 10000 ns localparam DURATION = 10000; // Internal signals wire [3:0] out; // Internal storage elements reg clk = 0; reg rst = 0; // Generate clock signal: 1 / ((2 * 4.167) * 1 ns) = 119,990,400.77 Hz always begin #4.167 clk = ~clk; end // Instantiate gray code counter gray_counter gray (.clk(clk), .rst(rst), .count(out)); // Pulse reset line high at the beginning initial begin #10 rst = 1; #1 rst = 0; end // Run simulation (output to .vcd file) initial begin // Create simulation output file $dumpfile("gray-code-counter_tb.vcd"); $dumpvars(0, gray_counter_tb); // Wait for given amount of time for simulation to complete #(DURATION) // Notify and end simulation $display("Finished!"); $finish; end endmodule ================================================ FILE: 10-metastability/example-01-metastability-test/apio.ini ================================================ [env] board = icestick ================================================ FILE: 10-metastability/example-01-metastability-test/metastability-test.pcf ================================================ # PMOD I/O set_io clk_in 87 set_io sig_in 88 set_io clk_out 90 set_io sig_out 91 ================================================ FILE: 10-metastability/example-01-metastability-test/metastability-test.v ================================================ // Input 2 MHz clock and 1 MHz sqaure wave signal. Phase shift the // 1 MHz signal to force metastability. // // Connections // Signal | IceStick | AnalogDiscovery 2 | Oscilloscope // ---------|---------------|-------------------|--------------- // VCC | 3.3V | vary 0.7 to 3.3V | // GND | GND | GND | ground clips // clk_in | 87 | W1 | // sig_in | 88 | W2 | ch. 1 // clk_out | 90 | | ch. 2 // sig_out | 91 | | ch. 3 // // Date: December 18, 2021 // Author: Shawn Hymel // License: 0BSD // Sample input waveform and register it module metastability_test ( // Inputs input clk_in, input sig_in, // Outputs output clk_out, output reg sig_out = 0 ); // Internal storage elements reg div_sig; reg pipe_0; // Pass input clock to output pin assign clk_out = clk_in; // Register input signal always @ (posedge clk_in) begin sig_out <= sig_in; //pipe_0 <= sig_in; //sig_out <= pipe_0; end endmodule ================================================ FILE: 10-metastability/example-02-better-clock-divider/apio.ini ================================================ [env] board = icestick ================================================ FILE: 10-metastability/example-02-better-clock-divider/clock-divider.v ================================================ // A better way to create a clock divider that does not use flip-flip outputs // as clock inputs to other flip-flops. Us the tick pulse in combination with // other logic to determine when to perform some action. // // Based on work by: https://github.com/Paebbels // // Date: December 16, 2021 // Author: Shawn Hymel // License: 0BSD // Divide the clock by outputting a pulse at the desired time module clock_divider #( // Parameters parameter MODULO = 6000000 ) ( // Inputs input clk, input rst, // Outputs output tick ); // Calculate number of bits needed for the counter localparam WIDTH = (MODULO == 1) ? 1 : $clog2(MODULO); // Internal storage elements reg [WIDTH-1:0] count = 0; // Tick is high for one clock cycle at max count assign tick = (count == MODULO - 1) ? 1'b1 : 1'b0; // Count up, reset on tick pulse always @ (posedge clk or posedge rst) begin if (rst | tick == 1'b1) begin count <= 0; end else begin count <= count + 1; end end endmodule ================================================ FILE: 10-metastability/example-02-better-clock-divider/clock-divider_tb.gtkw ================================================ [*] [*] GTKWave Analyzer v3.3.77 (w)1999-2016 BSI [*] Thu Dec 16 22:08:09 2021 [*] [dumpfile] "C:\Users\sgmustadio\Documents\apio\better-clock-divider\clock-divider_tb.vcd" [dumpfile_mtime] "Thu Dec 16 22:08:03 2021" [dumpfile_size] 5375201 [savefile] "C:\Users\sgmustadio\Documents\apio\better-clock-divider\clock-divider_tb.gtkw" [timestart] 0 [size] 1000 600 [pos] -1 -1 *-25.000000 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [treeopen] clock_divider_tb. [sst_width] 211 [signals_width] 110 [sst_expanded] 1 [sst_vpaned_height] 154 @28 clock_divider_tb.clk clock_divider_tb.rst clock_divider_tb.tick @25 clock_divider_tb.uut.count[6:0] [pattern_trace] 1 [pattern_trace] 0 ================================================ FILE: 10-metastability/example-02-better-clock-divider/clock-divider_tb.v ================================================ // Test clock divider operation. The divider output (tick) should // pulse once at each divided cycle. // // Date: December 16, 2021 // Author: Shawn Hymel // License: 0BSD // Define timescale `timescale 1 us / 10 ps // Define our testbench module clock_divider_tb(); // Settings localparam MODULO = 120; // 12 MHz / 120 = 100 kHz ticks // Internal signals wire tick; // Internal storage elements reg clk = 0; reg rst = 0; // Simulation time: 10000 * 1 us = 10 ms localparam DURATION = 10000; // Generate clock signal always begin #0.04167 clk = ~clk; end // Instantiate clock divider clock_divider #( .MODULO(MODULO) ) uut ( .clk(clk), .rst(rst), .tick(tick) ); // Test control: run for some time, toggle reset, let run some more initial begin #10 rst = 1; #1 rst = 0; end // Run simulation (output to .vcd file) initial begin // Create simulation output file $dumpfile("clock-divider_tb.vcd"); $dumpvars(0, clock_divider_tb); // Wait for given amount of time for simulation to complete #(DURATION) // Notify and end simulation $display("Finished!"); $finish; end endmodule ================================================ FILE: 10-metastability/example-02-better-clock-divider/slow-blink.pcf ================================================ # Oscillator set_io clk 21 # LEDs set_io led 99 # PMOD I/O set_io -pullup yes rst_btn 78 ================================================ FILE: 10-metastability/example-02-better-clock-divider/slow-blink.v ================================================ // Slow blink example using the better clock divider. // // Based on work by: https://github.com/Paebbels // // Date: December 16, 2021 // Author: Shawn Hymel // License: 0BSD // Slow blink top level design module slow_blink ( // Inputs input clk, input rst_btn, // Outputs output reg led ); // Settings localparam MODULO = 6000000; // Toggle LED every 0.5 s with 12 MHz clk // Internal signals wire rst; wire tick; // Invert reset button assign rst = ~rst_btn; // Instantiate clock divider clock_divider #( .MODULO(MODULO) ) uut ( .clk(clk), .rst(rst), .tick(tick) ); // Example using clock divider to blink LED always @ (posedge clk or posedge rst) begin if (rst == 1'b1) begin led <= 1'b0; end else if (tick == 1'b1) begin led <= ~led; end end endmodule ================================================ FILE: 10-metastability/example-03-better-debouncer/apio.ini ================================================ [env] board = icestick ================================================ FILE: 10-metastability/example-03-better-debouncer/debouncer.v ================================================ // Debounce signal logic without the use of a clock divider. // // Based on: https://forum.digikey.com/t/debounce-logic-circuit-vhdl/12573 // // Date: December 16, 2021 // Author: Shawn Hymel // License: 0BSD // Debouncing logic module debouncer #( // Parameters parameter MAX_CLK_COUNT = 120000 // 10 ms with 12 MHz clock ) ( // Inputs input clk, input rst, input sig, // Outputs output reg out ); // Calculate number of bits needed for the counter localparam WIDTH = $clog2(MAX_CLK_COUNT + 1); // Internal signals wire sig_edge; // Internal storage elements reg ff_1; reg ff_2; reg [WIDTH-1:0] count = 0; // Counter starts when outputs of the two flip-flops are different assign sig_edge = ff_1 ^ ff_2; // Logic to sample signal after a period of time always @ (posedge clk or posedge rst) begin // Reset flip-flops if (rst == 1'b1) begin ff_1 <= 0; ff_2 <= 0; out <= 0; // If rising or falling edge on signal, run counter and sample again end else begin ff_1 <= sig; ff_2 <= ff_1; if (sig_edge == 1'b1) begin count <= 0; end else if (count < MAX_CLK_COUNT) begin count <= count + 1; end else begin out <= ff_2; end end end endmodule ================================================ FILE: 10-metastability/example-03-better-debouncer/debouncer_tb.gtkw ================================================ [*] [*] GTKWave Analyzer v3.3.77 (w)1999-2016 BSI [*] Thu Dec 16 22:04:13 2021 [*] [dumpfile] "C:\Users\sgmustadio\Documents\apio\better-debounce\debouncer_tb.vcd" [dumpfile_mtime] "Thu Dec 16 17:47:23 2021" [dumpfile_size] 4164257 [savefile] "C:\Users\sgmustadio\Documents\apio\better-debounce\debouncer_tb.gtkw" [timestart] 0 [size] 1108 600 [pos] -135 173 *-31.000000 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [treeopen] debouncer_tb. [sst_width] 211 [signals_width] 110 [sst_expanded] 1 [sst_vpaned_height] 154 @28 debouncer_tb.clk debouncer_tb.rst debouncer_tb.inc_btn @22 debouncer_tb.uut.count[8:0] @28 debouncer_tb.out [pattern_trace] 1 [pattern_trace] 0 ================================================ FILE: 10-metastability/example-03-better-debouncer/debouncer_tb.v ================================================ // Testbench for button debounce design // // Date: November 16, 2021 // Author: Shawn Hymel // License: 0BSD // Define timescale `timescale 1 us / 10 ps // Define our testbench module debouncer_tb(); // Internal signals wire out; // Storage elements (buttons are active low!) reg clk = 0; reg rst = 0; reg inc_btn = 1; integer i; // Used in for loop integer j; // Used in for loop integer prev_inc; // Previous increment button state integer nbounces; // Holds random number integer rdelay; // Holds random number // Simulation time: 10000 * 1 us = 10 ms localparam DURATION = 10000; // Generate clock signal (about 12 MHz) always begin #0.04167 clk = ~clk; end // Instantiate debounce/counter module (use about 40 us wait time) debouncer #( .MAX_CLK_COUNT(480) ) uut ( .clk(clk), .rst(rst), .sig(inc_btn), .out(out) ); // Test control: pulse reset and create some (bouncing) button presses initial begin // Pulse reset #10 rst = 1; #1 rst = 0; // We can use for loops in simulation! for (i = 0; i < 32; i = i + 1) begin // Wait some time before pressing button #1000 // Simulate a bouncy/noisy button press // $urandom generates a 32-bit unsigned (pseudo) random number // "% 10" is "modulo 10" prev_inc = inc_btn; nbounces = $urandom % 20; for (j = 0; j < nbounces; j = j + 1) begin #($urandom % 10) inc_btn = ~inc_btn; end // Make sure button ends up in the opposite state inc_btn = ~prev_inc; end end // Run simulation (output to .vcd file) initial begin // Create simulation output file $dumpfile("debouncer_tb.vcd"); $dumpvars(0, debouncer_tb); // Wait for given amount of time for simulation to complete #(DURATION) // Notify and end simulation $display("Finished!"); $finish; end endmodule ================================================ FILE: 10-metastability/solution-async-fifo/apio.ini ================================================ [env] board = icestick ================================================ FILE: 10-metastability/solution-async-fifo/async-fifo.v ================================================ // Implementation of Clifford Cummings's asynchronous FIFO design from the paper // at http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf // // Top-level asynchronous FIFO module to be used in designs. // // Notes: // - w_* means something is in the "write clock domain" // - r_* means something is in the "read clock domain" // - Single memory element is DATA_SIZE bits wide // - Memory is 2^ADDR_SIZE elements deep // // Date: December 11, 2021 // Author: Shawn Hymel // License: 0BSD // Asynchronous FIFO module module async_fifo #( // Parameters parameter DATA_SIZE = 8, // Number of data bits parameter ADDR_SIZE = 4 // Number of bits for address ) ( // Inputs input [DATA_SIZE-1:0] w_data, // Data to be written to FIFO input w_en, // Write data and increment addr. input w_clk, // Write domain clock input w_rst, // Write domain reset input r_en, // Read data and increment addr. input r_clk, // Read domain clock input r_rst, // Read domain reset // Outputs output w_full, // Flag: 1 if FIFO is full output reg [DATA_SIZE-1:0] r_data, // Data to be read from FIFO output r_empty // Flag: 1 if FIFO is empty ); // Constants localparam FIFO_DEPTH = (1 << ADDR_SIZE); // Internal signals wire [ADDR_SIZE-1:0] w_addr; wire [ADDR_SIZE:0] w_gray; wire [ADDR_SIZE-1:0] r_addr; wire [ADDR_SIZE:0] r_gray; // Internal storage elements reg [ADDR_SIZE:0] w_syn_r_gray; reg [ADDR_SIZE:0] w_syn_r_gray_pipe; reg [ADDR_SIZE:0] r_syn_w_gray; reg [ADDR_SIZE:0] r_syn_w_gray_pipe; // Declare memory reg [DATA_SIZE-1:0] mem [0:FIFO_DEPTH-1]; //-------------------------------------------------------------------------- // Dual-port memory (should be inferred as block RAM) // Write data logic for dual-port memory (separate write clock) // Do not write if FIFO is full! always @ (posedge w_clk) begin if (w_en & ~w_full) begin mem[w_addr] <= w_data; end end // Read data logic for dual-port memory (separate read clock) // Do not read if FIFO is empty! always @ (posedge r_clk) begin if (r_en & ~r_empty) begin r_data <= mem[r_addr]; end end //-------------------------------------------------------------------------- // Synchronizer logic // Pass read-domain Gray code pointer to write domain always @ (posedge w_clk or posedge w_rst) begin if (w_rst == 1'b1) begin w_syn_r_gray_pipe <= 0; w_syn_r_gray <= 0; end else begin w_syn_r_gray_pipe <= r_gray; w_syn_r_gray <= w_syn_r_gray_pipe; end end // Pass write-domain Gray code pointer to read domain always @ (posedge r_clk or posedge r_rst) begin if (r_rst == 1'b1) begin r_syn_w_gray_pipe <= 0; r_syn_w_gray <= 0; end else begin r_syn_w_gray_pipe <= w_gray; r_syn_w_gray <= r_syn_w_gray_pipe; end end //-------------------------------------------------------------------------- // Instantiate incrementer and full/empty checker modules // Write address increment and full check module w_ptr_full #(.ADDR_SIZE(ADDR_SIZE)) w_ptr_full ( .w_syn_r_gray(w_syn_r_gray), .w_inc(w_en), .w_clk(w_clk), .w_rst(w_rst), .w_addr(w_addr), .w_gray(w_gray), .w_full(w_full) ); // Read address increment and empty check module r_ptr_empty #(.ADDR_SIZE(ADDR_SIZE)) r_ptr_empty ( .r_syn_w_gray(r_syn_w_gray), .r_inc(r_en), .r_clk(r_clk), .r_rst(r_rst), .r_addr(r_addr), .r_gray(r_gray), .r_empty(r_empty) ); endmodule ================================================ FILE: 10-metastability/solution-async-fifo/async-fifo_tb.gtkw ================================================ [*] [*] GTKWave Analyzer v3.3.77 (w)1999-2016 BSI [*] Sat Dec 11 20:21:30 2021 [*] [dumpfile] "C:\Users\sgmustadio\Documents\apio\fifo_test_03\async-fifo_tb.vcd" [dumpfile_mtime] "Sat Dec 11 20:20:07 2021" [dumpfile_size] 5748705 [savefile] "C:\Users\sgmustadio\Documents\apio\fifo_test_03\async-fifo_tb.gtkw" [timestart] 0 [size] 1707 897 [pos] -1 -1 *-21.000000 6927000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [sst_width] 211 [signals_width] 118 [sst_expanded] 1 [sst_vpaned_height] 259 @28 async_fifo_tb.w_clk async_fifo_tb.w_rst @22 async_fifo_tb.w_data[7:0] @28 async_fifo_tb.w_en @29 async_fifo_tb.w_full @28 async_fifo_tb.r_clk async_fifo_tb.r_rst @22 async_fifo_tb.r_data[7:0] @28 async_fifo_tb.r_en async_fifo_tb.r_empty [pattern_trace] 1 [pattern_trace] 0 ================================================ FILE: 10-metastability/solution-async-fifo/async-fifo_tb.v ================================================ // Simulation of Clifford Cummings's asynchronous FIFO design from the paper // at http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf // // Testbench to verify RTL design of the asynchronous FIFO design. Note that // this does not check for things like metastability and glitches! You would // likely need to use a gate-level simulation tool for that. // // Notes: // - w_* means something is in the "write clock domain" // - r_* means something is in the "read clock domain" // - Single memory element is DATA_SIZE bits wide // - Memory is 2^ADDR_SIZE elements deep // // Date: December 11, 2021 // Author: Shawn Hymel // License: 0BSD // Define timescale `timescale 1 us / 10 ps // Define our testbench module async_fifo_tb(); // Settings localparam DATA_SIZE = 8; localparam ADDR_SIZE = 4; // Internal signals wire [DATA_SIZE-1:0] r_data; wire r_empty; wire r_full; // Internal storage elements reg r_en = 0; reg r_clk = 0; reg r_rst = 0; reg [DATA_SIZE-1:0] w_data; reg w_en = 0; reg w_clk = 0; reg w_rst = 0; // Variables integer i; // Simulation time: 10000 * 1 us = 10 ms localparam DURATION = 10000; // Generate read clock signal (about 12 MHz) always begin #0.04167 r_clk = ~r_clk; end // Generate write clock signal (5 MHz) always begin #0.1 w_clk = ~w_clk; end // Instantiate FIFO async_fifo #( .DATA_SIZE(DATA_SIZE), .ADDR_SIZE(ADDR_SIZE) ) uut ( .w_data(w_data), .w_en(w_en), .w_clk(w_clk), .w_rst(w_rst), .r_en(r_en), .r_clk(r_clk), .r_rst(r_rst), .w_full(w_full), .r_data(r_data), .r_empty(r_empty) ); // Test control: write and read data to/from FIFO initial begin // Pulse resets high to initialize memory and counters #0.1 w_rst = 1; r_rst = 1; #0.01 w_rst = 0; r_rst = 0; // Write some data to the FIFO for (i = 0; i < 4; i = i + 1) begin #0.2 w_data = i; w_en = 1'b1; end #0.2 w_en = 1'b0; // Try to read more than what's in the FIFO for (i = 0; i < 6; i = i + 1) begin #0.08334 r_en = 1'b1; end #0.08334 r_en = 1'b0; // Fill up FIFO (and then some) for (i = 0; i < 18; i = i + 1) begin #0.2 w_en = 1'b1; w_data = i; end #0.2 w_en = 1'b0; // Read everything in the FIFO (and then some) for (i = 0; i < 18; i = i + 1) begin #0.08334 r_en = 1'b1; end #0.08334 r_en = 1'b0; end // Run simulation initial begin // Create simulation output file $dumpfile("async-fifo_tb.vcd"); $dumpvars(0, async_fifo_tb); // Wait for given amount of time for simulation to complete #(DURATION) // Notify and end simulation $display("Finished!"); $finish; end endmodule ================================================ FILE: 10-metastability/solution-async-fifo/dummy.pcf ================================================ # Dummy PCF signals to test synthesis set_io r_en 112 set_io r_clk 113 set_io w_data[0] 114 set_io w_data[1] 115 set_io w_data[2] 116 set_io w_data[3] 117 set_io w_data[4] 118 set_io w_data[5] 119 set_io w_data[6] 44 set_io w_data[7] 45 set_io w_en 47 set_io w_clk 48 set_io w_rst 56 set_io r_rst 60 set_io r_data[0] 61 set_io r_data[1] 62 set_io r_data[2] 78 set_io r_data[3] 79 set_io r_data[4] 80 set_io r_data[5] 81 set_io r_data[6] 87 set_io r_data[7] 88 set_io r_empty 99 set_io w_full 98 ================================================ FILE: 10-metastability/solution-async-fifo/r-ptr-empty.v ================================================ // Implementation of Clifford Cummings's asynchronous FIFO design from the paper // at http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf // // Module to incrememnt the read address and determine if the FIFO is empty. // // Notes: // - w_* means something is in the "write clock domain" // - r_* means something is in the "read clock domain" // // Date: December 11, 2021 // Author: Shawn Hymel // License: 0BSD // Increment read address and check if FIFO is empty module r_ptr_empty #( // Parameters parameter ADDR_SIZE = 4 // Number of bits for address ) ( // Inputs input [ADDR_SIZE:0] r_syn_w_gray, // Synced write Gray pointer input r_inc, // 1 to increment address input r_clk, // Read domain clock input r_rst, // Read domain reset // Outputs output [ADDR_SIZE-1:0] r_addr, // Mem address to read from output reg [ADDR_SIZE:0] r_gray, // Gray address with +1 MSb output reg r_empty // 1 if FIFO is empty ); // Internal signals wire [ADDR_SIZE:0] r_gray_next; // Gray code version of address wire [ADDR_SIZE:0] r_bin_next; // Binary version of address wire r_empty_val; // FIFO is empty // Internal storage elements reg [ADDR_SIZE:0] r_bin; // Registered binary address // Drop extra most significant bit (MSb) for addressing into memory assign r_addr = r_bin[ADDR_SIZE-1:0]; // Be ready with next (incremented) address (if inc set and not empty) assign r_bin_next = r_bin + (r_inc & ~r_empty); // Convert next binary address to Gray code value assign r_gray_next = (r_bin_next >> 1) ^ r_bin_next; // If the synced write Gray code is equal to the current read Gray code, // then the pointers have caught up to each other and the FIFO is empty assign r_empty_val = (r_gray_next == r_syn_w_gray); // Register the binary and Gray code pointers in the read clock domain always @ (posedge r_clk or posedge r_rst) begin if (r_rst == 1'b1) begin r_bin <= 0; r_gray <= 0; end else begin r_bin <= r_bin_next; r_gray <= r_gray_next; end end // Register the empty flag always @ (posedge r_clk or posedge r_rst) begin if (r_rst == 1'b1) begin r_empty <= 1'b1; end else begin r_empty <= r_empty_val; end end endmodule ================================================ FILE: 10-metastability/solution-async-fifo/w-ptr-full.v ================================================ // Implementation of Clifford Cummings's asynchronous FIFO design from the paper // at http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf // // Module to incrememnt the write address and determine if the FIFO is full. // // Notes: // - w_* means something is in the "write clock domain" // - r_* means something is in the "read clock domain" // // Date: December 11, 2021 // Author: Shawn Hymel // License: 0BSD // Increment write address and check if FIFO is full module w_ptr_full #( // Parameters parameter ADDR_SIZE = 4 // Number of bits for address ) ( // Inputs input [ADDR_SIZE:0] w_syn_r_gray, // Synced read Gray pointer input w_inc, // 1 to increment address input w_clk, // Write domain clock input w_rst, // Write domain reset // Outputs output [ADDR_SIZE-1:0] w_addr, // Mem address to write to output reg [ADDR_SIZE:0] w_gray, // Gray adress with +1 MSb output reg w_full // 1 if FIFO is full ); // Internal signals wire [ADDR_SIZE:0] w_gray_next; // Gray code version of address wire [ADDR_SIZE:0] w_bin_next; // Binary version of address wire w_full_val; // FIFO is full // Internal storage elements reg [ADDR_SIZE:0] w_bin; // Registered binary address // Drop extra most significant bit (MSb) for addressing into memory assign w_addr = w_bin[ADDR_SIZE-1:0]; // Be ready with next (incremented) address (if inc set and not full) assign w_bin_next = w_bin + (w_inc & ~w_full); // Convert next binary address to Gray code value assign w_gray_next = (w_bin_next >> 1) ^ w_bin_next; // Compare write Gray code to synced read Gray code to see if FIFO is full // If: extra MSb of read and write Gray codes are not equal AND // 2nd MSb of read and write Gray codes are not equal AND // the rest of the bits are equal // Then: address pointers are same with write pointer ahead by 2^ADDR_SIZE // elements (i.e. wrapped around), so FIFO is full. assign w_full_val = ((w_gray_next[ADDR_SIZE] != w_syn_r_gray[ADDR_SIZE]) && (w_gray_next[ADDR_SIZE-1] != w_syn_r_gray[ADDR_SIZE-1]) && (w_gray_next[ADDR_SIZE-2:0] == w_syn_r_gray[ADDR_SIZE-2:0])); // Register the binary and Gray code pointers in the write clock domain always @ (posedge w_clk or posedge w_rst) begin if (w_rst == 1'b1) begin w_bin <= 0; w_gray <= 0; end else begin w_bin <= w_bin_next; w_gray <= w_gray_next; end end // Register the full flag always @ (posedge w_clk or posedge w_rst) begin if (w_rst == 1'b1) begin w_full <= 1'b0; end else begin w_full <= w_full_val; end end endmodule ================================================ FILE: 11-risc-v-softcore-cpu/example-01-blinky/Makefile ================================================ include ../../learn-fpga/FemtoRV/FIRMWARE/makefile.inc ================================================ FILE: 11-risc-v-softcore-cpu/example-01-blinky/main.c ================================================ // Blink 2 LEDs and print info to the serial terminal. // // Date: December 19, 2021 // Author: Shawn Hymel // License: 0BSD #include int main() { while(1) { printf("Hello, world!\r\n"); printf("Freq: %d MHz\r\n", FEMTORV32_FREQ); *(volatile uint32_t*)(0x400004) = 3; delay(500); *(volatile uint32_t*)(0x400004) = 0; delay(500); } return 0; } ================================================ FILE: 11-risc-v-softcore-cpu/solution-buttons/Makefile ================================================ include ../../learn-fpga/FemtoRV/FIRMWARE/makefile.inc ================================================ FILE: 11-risc-v-softcore-cpu/solution-buttons/main.c ================================================ // Count on LEDs as long as button is pressed. // // Date: December 19, 2021 // Author: Shawn Hymel // License: 0BSD #include int main() { int count = 0; while (1) { if ((IO_IN(IO_BUTTONS) & 1) == 0) { count = (count + 1) % 16; IO_OUT(IO_LEDS, count); delay(250); } } return 0; } ================================================ FILE: 12-risc-v-custom-peripheral/example-01-pwm/C/pwm_test/Makefile ================================================ include ../../learn-fpga/FemtoRV/FIRMWARE/makefile.inc ================================================ FILE: 12-risc-v-custom-peripheral/example-01-pwm/C/pwm_test/main.c ================================================ // Slowly increase the brightness of the PWM-controlled LED over and over. // // Date: December 19, 2021 // Author: Shawn Hymel // License: 0BSD #include int main() { while (1) { for (int i = 0; i < 4096; i++) { *(volatile uint32_t*)(0x404000) = i; delay(1); } } return 0; } ================================================ FILE: 12-risc-v-custom-peripheral/example-01-pwm/RTL/apio.ini ================================================ [env] board = icestick ================================================ FILE: 12-risc-v-custom-peripheral/example-01-pwm/RTL/pwm.pcf ================================================ # Oscillator set_io clk 21 # LEDs set_io led[0] 99 set_io led[1] 98 set_io led[2] 97 set_io led[3] 96 ================================================ FILE: 12-risc-v-custom-peripheral/example-01-pwm/RTL/pwm.v ================================================ // PWM driver for FemtoRV: https://github.com/BrunoLevy/learn-fpga // // Controls brightness of first LED. Set duty cycle with wdata (0..4095). // // Date: December 15, 2021 // Author: Shawn Hymel // License: 0BSD // Control brightness of one of the LEDs module pwm #( // Parameters parameter WIDTH = 12 // Default PWM values 0..4095 ) ( // Inputs input clk, input wstrb, // Write strobe input sel, // Select (read/write ignored if low) input [31:0] wdata, // Data to be written (to driver) // Outputs output [3:0] led ); // Internal storage elements reg pwm_led = 1'b0; reg [WIDTH-1:0] pwm_count = 0; reg [WIDTH-1:0] count = 0; // Only control the first LED assign led[0] = pwm_led; // Update PWM duty cycle always @ (posedge clk) begin // If sel is high, record duty cycle count on strobe if (sel && wstrb) begin pwm_count <= wdata[WIDTH-1:0]; count <= 0; // Otherwise, continuously count and flash LED as necessary end else begin count <= count + 1; if (count < pwm_count) begin pwm_led <= 1'b1; end else begin pwm_led <= 1'b0; end end end endmodule ================================================ FILE: 12-risc-v-custom-peripheral/example-01-pwm/RTL/pwm_tb.gtkw ================================================ [*] [*] GTKWave Analyzer v3.3.77 (w)1999-2016 BSI [*] Wed Dec 15 20:37:05 2021 [*] [dumpfile] "C:\Users\sgmustadio\Documents\apio\pwm_01\pwm_tb.vcd" [dumpfile_mtime] "Wed Dec 15 20:32:44 2021" [dumpfile_size] 5154 [savefile] "C:\Users\sgmustadio\Documents\apio\pwm_01\pwm_tb.gtkw" [timestart] 3820000 [size] 1000 600 [pos] 68 104 *-19.000000 4542030 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [treeopen] pwm_tb. [sst_width] 211 [signals_width] 142 [sst_expanded] 1 [sst_vpaned_height] 154 @28 pwm_tb.clk pwm_tb.sel pwm_tb.wstrb @22 pwm_tb.wdata[31:0] pwm_tb.uut.pwm_count[3:0] @23 pwm_tb.uut.count[3:0] @800022 pwm_tb.led[3:0] @28 (0)pwm_tb.led[3:0] (1)pwm_tb.led[3:0] (2)pwm_tb.led[3:0] (3)pwm_tb.led[3:0] @1001200 -group_end [pattern_trace] 1 [pattern_trace] 0 ================================================ FILE: 12-risc-v-custom-peripheral/example-01-pwm/RTL/pwm_tb.v ================================================ // Simulation of PWM driver. // // Date: December 15, 2021 // Author: Shawn Hymel // License: 0BSD // Define timescale `timescale 1 ns / 10 ps // Testbench module pwm_tb(); // Simulation time: 10000 * 1 us = 10 ms localparam DURATION = 10000; // Internal signals wire [3:0] led; // Internal storage elements reg clk = 0; reg wstrb = 0; reg sel = 0; reg [31:0] wdata = 0; // Generate read clock signal (about 12 MHz) always begin #41.667 clk = ~clk; end // Instantiate PWM driver pwm #( .WIDTH(4) ) uut ( .clk(clk), .wstrb(wstrb), .sel(sel), .wdata(wdata), .led(led) ); // Test control: send values to PWM driver and watch LED initial begin // Wait some time to see how LED initializes #1500 // Write 0% duty cycle wstrb = 1; sel = 1; wdata = 0; #100 wstrb = 0; sel = 0; // Write ~33% duty cycle #1500 wstrb = 1; sel = 1; wdata = 5; #100 wstrb = 0; sel = 0; // Write 100% duty cycle #1500 wstrb = 1; sel = 1; wdata = 15; #100 wstrb = 0; sel = 0; end // Run simulation (output to .vcd file) initial begin // Create simulation output file $dumpfile("pwm_tb.vcd"); $dumpvars(0, pwm_tb); // Wait for given amount of time for simulation to complete #(DURATION) // Notify and end simulation $display("Finished!"); $finish; end endmodule ================================================ FILE: README.md ================================================ # Introduction to FPGA Welcome to the demo code and solutions section of my Introduction to FPGA course! This repository houses all of the example code and solutions that you may use as references when working through the FPGA examples. [![Introduction to FPGA YouTube Series](https://raw.githubusercontent.com/ShawnHymel/introduction-to-fpga/main/images/Intro%20to%20FPGA%20Part%201_Thumbnail.png)](https://www.youtube.com/watch?v=lLg1AgA2Xoo&list=PLEBQazB0HUyT1WmMONxRZn9NmQ_9CIKhb) This course is hosted on YouTube that you may take for free. All you need to do is watch the videos and complete the challenge issued at the end of each video. I highly recommend you try each challenge before peeking at the solutions here. The first video in the series is found here: [Introduction to FPGA Part 1 - What is an FPGA? | Digi-Key Electronics](https://www.youtube.com/watch?v=lLg1AgA2Xoo&list=PLEBQazB0HUyT1WmMONxRZn9NmQ_9CIKhb) Written guides that explain the solutions can be found here: 1. [What is an FPGA?](https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-1-what-is-an-fpga/3ee5f6c8fa594161a655a9f960060893) 2. [Toolchain Setup](https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-2-toolchain-setup/563a9518cd11466fb6a75cf3cb684d6d) 3. [Getting Started with Verilog](https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-3-getting-started-with-verilog/9d9dbff29a4b45728521b2664bbd1df4) 4. [Clocks and Procedural Assignments](https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-4-clocks-and-procedural-assignments/356e12284daf48b5bd9b80af8a6ac5b8) 5. [Finite State Machine (FSM)](https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-5-finite-state-machine-fsm/4d83e63da76044af9acc8aa7dcf07c22) 6. [Verilog Modules and Parameters](https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-6-verilog-modules-and-parameters/c7d4d01274be43278d8bc531e6b7acb7) 7. [Verilog Testbenches and Simulation](https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-7-verilog-testbenches-and-simulation/1b741d1b8b864afeacbe28075b1427cd) 8. [Memory and Block RAM](https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-8-memory-and-block-ram/df7bcadef0de430ab89d0d9c21e3a14c) 9. [Phase-Locked Loop (PLL) and Glitches](https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-9-phaselocked-loop-pll-and-glitches/2028ce62001b4cb69335f48e127fa366) 10. [Metastability and FIFO](https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-10-metastability-and-fifo/74884ed134474e008a1e444ea9dacb0f) 11. [RISC-V Softcore Processor](https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-11-risc-v-softcore-processor/f0511ddb538f444cae08f7bc43a74dcc) 12. [RISC-V Peripheral](https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-12-risc-v-custom-peripheral/148dba8ecafe49a1ac7e13641088af4c) ## Directory Structure Examples and solutions are housed in dirctories that correspond to each chapter or video number. For example, if you watch "Intro to FPGA Part 3 - Verilog Gate Logic," you should refer to the directory *03-verilog-gate-logic*. In each directory, you will find example projects. Demonstrations used in the video are listed as *example-\** and the solution to that part's challenge is listed as *solution-\**. The only exception to this is the *images* directory, which is where I keep images for this repository. ## License All code in this repository, unless otherwise noted, is licensed under the [Zero-Clause BSD / Free Public License 1.0.0 (0BSD)](https://opensource.org/licenses/0BSD). Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.