Tinkering with FPGAs


 

In this video, I present a high-level overview of the Digilent Arty-S7 board with Xilinx Spartan 7 FPGA, targeted towards hobbyists and makers. It includes a walk-through of the process of creating and implementing a digital design in an FPGA from writing RTL (in Verilog), simulation, synthesizing, implementing and uploading the bitstream to the device using Xilinx Vivado.

Links to some related Digilent and Xilinx resources:

https://www.xilinx.com/products/boards-and-kits/1-pnziih.html

https://digilent.com/reference/programmable-logic/arty-s7/start


Verilog HDL Code for the example design featured in this video:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:   Tinker Foundry
// Engineer:  Tinker Founder
// 
// Create Date: 05/06/2023 01:39:13 PM
// Module Name: tinker
// Target Devices: Arty-S7 Board
// Description: 
// Example design that will increment a 4-bit counter if SWITCH0 is off, and
// decrement the counter value if SWITCH0 is on.
// Counter will increment/decrement every 25000000 clock cycles if SWITCH1 is off (fast count)
// and will increment/decrement every 100000000 clock cycles if SWITCH1 is on (slow count)
// Counter will be displayed using LED0..3 on the board
//
//////////////////////////////////////////////////////////////////////////////////
module tinker(
  iCLK,  // input clock
  iRST,  // reset
  iSW0,  // SWITCH0 input on or off (1 or 0)
  iSW1,  // SWITCH1 input on or off (1 or 0)
  oLED0, // LED0 output
  oLED1, // LED1 output
  oLED2, // LED2 output
  oLED3  // LED3 output
);
// ------------------
// parameters
// ------------------
parameter  fast_cnt_param = 28'd25000000;
parameter  slow_cnt_param = 28'd100000000;
// ------------------
// port declarations
// ------------------
input     iCLK;  // clock
input     iRST;  // reset in
input     iSW0;  // switch 0
input     iSW1;  // switch 1
output    oLED0; // LED0
output    oLED1; // LED1
output    oLED2; // LED2
output    oLED3; // LED3
// -------------------
// signal declarations
// -------------------
reg  [3:0]  counter0q;          // main counter registers used for output
reg  [27:0] large_counter0q;    // large counter used to count clock cycles
reg         count_pulse;        // pulse that is used to increment or decrement the main counter when large_counter reaches intended value
reg  [7:0]  switch0_debounce0q; // set of registers used to "debounce" SWITCH0 input
reg  [7:0]  switch1_debounce0q; // set of registers used to "debounce" SWITCH0 input
reg         switch0_val0q;      // debounced value of SWITCH0
reg         switch1_val0q;      // debounced value of SWITCH1
// -------------------
// main code
// -------------------
// Debounce logic... we will push SWITCH0 bit input into a shift register
// only when all 8 bits of the shift register are the same do we recognize the value
always @ (posedge iCLK)
begin
  if (iRST == 1'b1)
     switch0_debounce0q <= 8'b0;
  else
     begin
     switch0_debounce0q[0] <= iSW0; // SWITCH0 value pushed into bit 0 of shift register
     switch0_debounce0q[1] <= switch0_debounce0q[0];
     switch0_debounce0q[2] <= switch0_debounce0q[1];
     switch0_debounce0q[3] <= switch0_debounce0q[2];
     switch0_debounce0q[4] <= switch0_debounce0q[3];
     switch0_debounce0q[5] <= switch0_debounce0q[4];
     switch0_debounce0q[6] <= switch0_debounce0q[5];
     switch0_debounce0q[7] <= switch0_debounce0q[6];     
     end
end  
// detect all bits of debounce shift register are the same to assign value to switch0
always @ (posedge iCLK)
begin
  if (iRST == 1'b1) // reset
     switch0_val0q <= 1'b0; // default of 0
  else if (switch0_debounce0q == 8'b00000000) // if all 0s, then assign 0
     switch0_val0q <= 1'b0;
  else if (switch0_debounce0q == 8'b11111111) // if all 1s, then assign 1
     switch0_val0q <= 1'b1;
  else // otherwise, keep the same value
     switch0_val0q <= switch0_val0q; 
end
// Now do the same debouncing for SWITCH1
always @ (posedge iCLK)
begin
  if (iRST == 1'b1)
     switch1_debounce0q <= 8'b0;
  else
     begin
     switch1_debounce0q[0] <= iSW1; // SWITCH1 value pushed into bit 0 of shift register
     switch1_debounce0q[1] <= switch1_debounce0q[0];
     switch1_debounce0q[2] <= switch1_debounce0q[1];
     switch1_debounce0q[3] <= switch1_debounce0q[2];
     switch1_debounce0q[4] <= switch1_debounce0q[3];
     switch1_debounce0q[5] <= switch1_debounce0q[4];
     switch1_debounce0q[6] <= switch1_debounce0q[5];
     switch1_debounce0q[7] <= switch1_debounce0q[6];     
     end
end  
// detect all bits of debounce shift register are the same to assign value to switch0
always @ (posedge iCLK)
begin
  if (iRST == 1'b1) // reset
     switch1_val0q <= 1'b0; // default of 0
  else if (switch1_debounce0q == 8'b00000000) // if all 0s, then assign 0
     switch1_val0q <= 1'b0;
  else if (switch1_debounce0q == 8'b11111111) // if all 1s, then assign 1
     switch1_val0q <= 1'b1;
  else // otherwise, keep the same value
     switch1_val0q <= switch1_val0q; 
end
// Implement large counter (28-bit) that counts clock cycles
// It will increment until it reaches the pre-defined limit selected by SWITCH1
// If SWITCH1 is 0, it will clear to 0 when it reaches a count of 25,000,000
// If SWITCH1 is 1, it will clear to 0 when it reaches a count of 100,000,000
always @ (posedge iCLK)
begin
  if (iRST == 1'b1)
    large_counter0q <= 28'd0;
  else if (switch1_val0q == 1'b0) // clears when counter reaches 25,000,000 otherwise increment by 1
    if (large_counter0q >= fast_cnt_param)
      large_counter0q <= 28'd0;
    else
      large_counter0q <= large_counter0q + 28'd1; // increment by 1
  else // clears when counter reaches 100,000,000 otherwise increment by 1
    if (large_counter0q >= slow_cnt_param)
      large_counter0q <= 28'd0;
    else
      large_counter0q <= large_counter0q + 28'd1;
end
// count_pulse - this pulses when large_counter0q reaches the terminal value selected by SWITCH1 (25,000,000 or 100,000,000)
// combinational logic
always @*
begin
  if (switch1_val0q == 1'b0) // fast counter
    if (large_counter0q == fast_cnt_param) // this lasts 1 cycle to create a high pulse
      count_pulse = 1'b1;
    else
      count_pulse = 1'b0;
  else // slow counter
    if (large_counter0q == slow_cnt_param) // this lasts 1 cycle to create a high pulse
      count_pulse = 1'b1;
    else
      count_pulse = 1'b0;
end
// Implement up/down main counter
// Counter value will increment or decrement when count_pulse is high
// increment if switch0 is low, otherwise decrement
// the counter will wrap-around if we increment beyond 15 or decrement below 0
always @ (posedge iCLK)
begin
  if (iRST == 1'b1)
     counter0q <= 4'd0; // reset to 0
  else if (count_pulse) // this indicates to increment or decrement counter
     if (switch0_val0q == 1'b0) // increment
        counter0q <= counter0q + 4'd1;
     else // decrement otherwise
        counter0q <= counter0q - 4'd1;
end
// -------------------
// output
// -------------------
// assign each LED to 1 bit of the 4-bit main counter
assign oLED0 = counter0q[0];
assign oLED1 = counter0q[1];
assign oLED2 = counter0q[2];
assign oLED3 = counter0q[3];
endmodule

Testbench code featured in this video:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: tinker_testbench
// Description: 
// Testbench for tinker.v
// 
//////////////////////////////////////////////////////////////////////////////////

module tinker_testbench(
);
    
reg clk;
reg rst;
reg sw0;
reg sw1;

wire led0;
wire led1;
wire led2;
wire led3;    

// create testbench clock
initial begin
   clk = 1'b0;
   forever begin
     #10 clk = ~clk; // flip clock every 10ns
   end
end

// initialize reset
initial begin
   rst = 1'b1;
   #20 rst = 1'b0;
end

// SWITCH0
initial begin
   sw0 = 1'b0;
   #900 sw0 = 1'b1;
   #8000 sw0 = 1'b0;
end

// SWITCH1
initial begin
  sw1 = 1'b0;
  #2000 sw1 = 1'b1;
end

// DUT
tinker tinker_inst (
  .iCLK    (clk), // input clock
  .iRST    (rst), // reset
  .iSW0    (sw0), // SWITCH0 input on or off (1 or 0)
  .iSW1    (sw1), // SWITCH1 input on or off (1 or 0)
  .oLED0   (led0), // LED0 output
  .oLED1   (led1), // LED1 output
  .oLED2   (led2), // LED2 output
  .oLED3   (led3)  // LED3 output
);
 
endmodule



Comments

Popular posts from this blog

ESP32 Based Pulse-Oximeter using MAX30102

ESP32 Web Sockets - Transferring Image Raw Data

Heart Rate Measurement with ESP32 and Pulse Oximeter Module using FFT