Spring 2026 CE433 Course Project: An Pulse Sensor ASIC Design

The goal of this projet is to design a pulse sensor ASIC that includes an off-chip amplifier/filter circuit, an on-chip ADC, Picoblaze CPU, and a SPI module that directly communicates with an OLED display module (Pmod OLED: 128 x 32 Pixel Monochromatic OLED Display).

The product diagram can be found below.



The Pmod OLED schematic can be found below.



The pinout table can be found below. The CS, MOSI, and SCK pins are standard SPI pins. The fourth pin is D/C which is for Data/Command Control.



The D/C pin determines if the bytes are for the display or for the commands. The
SSD1306 datasheet shows the details.
From the datasheet, Section 8.1.3 MCU Serial Interface (4-wire SPI) describes how to use the fourth pin D/C.
SDIN is shifted into an 8-bit shift register on every rising edge of SCLK in the order of D7, D6, ... D0. D/C# is sampled on every eighth clock and the data byte in the shift register is written to the Graphic Display Data RAM (GDDRAM) or command register in the same clock.





However, for the Section 8.1.4 MCU Serial Interface (3-wire SPI), it requires the D/C command to be sent along with data as D8. This requires a different design of the driver in hardware and software to handle this bit.



The Pmod OLED example page provided an example package for the Nexys 3 board. We are using the Basys 3 board so some pins, connections, and the constraint file may need changes to be compatible with the board.
Open the top file of the design, the port declaration tells that DC is a fourth pin so it must use the 4-wire SPI timing diagram.



Bring the Picoblaze core to this design and write an assembly code to measure the digitized DO from the pulse sensor and count an average heart rate and display it on the OLED display module.

The pins of the PmodOLED is mapped to the JB pins as follows:







The following content in OledEX.v must be replaced by the initial begin/end block listed below since the format is not supported by Vivado.



initial begin
    // ================= Alphabet Screen =================
    // Row 0
    alphabet_screen[0][0]=8'h41; alphabet_screen[0][1]=8'h42;
    alphabet_screen[0][2]=8'h43; alphabet_screen[0][3]=8'h44;
    alphabet_screen[0][4]=8'h45; alphabet_screen[0][5]=8'h46;
    alphabet_screen[0][6]=8'h47; alphabet_screen[0][7]=8'h48;
    alphabet_screen[0][8]=8'h49; alphabet_screen[0][9]=8'h4A;
    alphabet_screen[0][10]=8'h4B; alphabet_screen[0][11]=8'h4C;
    alphabet_screen[0][12]=8'h4D; alphabet_screen[0][13]=8'h4E;
    alphabet_screen[0][14]=8'h4F; alphabet_screen[0][15]=8'h50;

    // Row 1
    alphabet_screen[1][0]=8'h51; alphabet_screen[1][1]=8'h52;
    alphabet_screen[1][2]=8'h53; alphabet_screen[1][3]=8'h54;
    alphabet_screen[1][4]=8'h55; alphabet_screen[1][5]=8'h56;
    alphabet_screen[1][6]=8'h57; alphabet_screen[1][7]=8'h58;
    alphabet_screen[1][8]=8'h59; alphabet_screen[1][9]=8'h5A;
    alphabet_screen[1][10]=8'h61; alphabet_screen[1][11]=8'h62;
    alphabet_screen[1][12]=8'h63; alphabet_screen[1][13]=8'h64;
    alphabet_screen[1][14]=8'h65; alphabet_screen[1][15]=8'h66;

    // Row 2
    alphabet_screen[2][0]=8'h67; alphabet_screen[2][1]=8'h68;
    alphabet_screen[2][2]=8'h69; alphabet_screen[2][3]=8'h6A;
    alphabet_screen[2][4]=8'h6B; alphabet_screen[2][5]=8'h6C;
    alphabet_screen[2][6]=8'h6D; alphabet_screen[2][7]=8'h6E;
    alphabet_screen[2][8]=8'h6F; alphabet_screen[2][9]=8'h70;
    alphabet_screen[2][10]=8'h71; alphabet_screen[2][11]=8'h72;
    alphabet_screen[2][12]=8'h73; alphabet_screen[2][13]=8'h74;
    alphabet_screen[2][14]=8'h75; alphabet_screen[2][15]=8'h76;

    // Row 3
    alphabet_screen[3][0]=8'h77; alphabet_screen[3][1]=8'h78;
    alphabet_screen[3][2]=8'h79; alphabet_screen[3][3]=8'h7A;
    alphabet_screen[3][4]=8'h30; alphabet_screen[3][5]=8'h31;
    alphabet_screen[3][6]=8'h32; alphabet_screen[3][7]=8'h33;
    alphabet_screen[3][8]=8'h34; alphabet_screen[3][9]=8'h35;
    alphabet_screen[3][10]=8'h36; alphabet_screen[3][11]=8'h37;
    alphabet_screen[3][12]=8'h38; alphabet_screen[3][13]=8'h39;
    alphabet_screen[3][14]=8'h7F; alphabet_screen[3][15]=8'h7F;

    // ================= Clear Screen =================
    for (r = 0; r < 4; r = r + 1)
        for (c = 0; c < 16; c = c + 1)
            clear_screen[r][c] = 8'h20;

    // ================= Digilent Screen =================
    // Row 0: "This is"
    digilent_screen[0][0]=8'h54; digilent_screen[0][1]=8'h68;
    digilent_screen[0][2]=8'h69; digilent_screen[0][3]=8'h73;
    digilent_screen[0][4]=8'h20; digilent_screen[0][5]=8'h69;
    digilent_screen[0][6]=8'h73; digilent_screen[0][7]=8'h20;
    digilent_screen[0][8]=8'h20; digilent_screen[0][9]=8'h20;
    digilent_screen[0][10]=8'h20; digilent_screen[0][11]=8'h20;
    digilent_screen[0][12]=8'h20; digilent_screen[0][13]=8'h20;
    digilent_screen[0][14]=8'h20; digilent_screen[0][15]=8'h20;

    // Row 1: "Digilent's"
    digilent_screen[1][0]=8'h44; digilent_screen[1][1]=8'h69;
    digilent_screen[1][2]=8'h67; digilent_screen[1][3]=8'h69;
    digilent_screen[1][4]=8'h6C; digilent_screen[1][5]=8'h65;
    digilent_screen[1][6]=8'h6E; digilent_screen[1][7]=8'h74;
    digilent_screen[1][8]=8'h27; digilent_screen[1][9]=8'h73;
    digilent_screen[1][10]=8'h20; digilent_screen[1][11]=8'h20;
    digilent_screen[1][12]=8'h20; digilent_screen[1][13]=8'h20;
    digilent_screen[1][14]=8'h20; digilent_screen[1][15]=8'h20;

    // Row 2: "PmodOLED"
    digilent_screen[2][0]=8'h50; digilent_screen[2][1]=8'h6D;
    digilent_screen[2][2]=8'h6F; digilent_screen[2][3]=8'h64;
    digilent_screen[2][4]=8'h4F; digilent_screen[2][5]=8'h4C;
    digilent_screen[2][6]=8'h45; digilent_screen[2][7]=8'h44;
    digilent_screen[2][8]=8'h20; digilent_screen[2][9]=8'h20;
    digilent_screen[2][10]=8'h20; digilent_screen[2][11]=8'h20;
    digilent_screen[2][12]=8'h20; digilent_screen[2][13]=8'h20;
    digilent_screen[2][14]=8'h20; digilent_screen[2][15]=8'h20;

    // Row 3: blank
    for (c = 0; c < 16; c = c + 1)
        digilent_screen[3][c] = 8'h20;
end

In addition to the modifications above, you also need to create a memory 'charLib' to store the charLib.coe data. You can tell that charLib is instantiated in the OledEX module







Here is the result of the example from Digilent.







Tasks:
Task 1: Turn the PmodOLEDCtrl.v and the submodules into a Basys3 compatible design and display any characters on the OLED to prove its functionality. There is no Picoblaze involved, only use the code from the example and make it Basys3 compatible. (50 points)
Task 2: Add Picoblaze to the design, probe AO, read the XADC's output and display the binary form on the on-board LEDs. (50 points)
Task 3: Use assembly and Picoblaze for frequency measurement and display heart rate on the OLED. (50 points)









References:
1. Pmod OLED product page
2. Pmod OLED support page
3. Pmod OLED examples page
3. SSD1306 datasheet