Skip to content

Final Project > Deliverables

7 Button Diatonic Chord Mono Synth ASIC > Functionality

alt text

My chip design emulates the functionality of a commercial synthesizer called the HiChord,

alt text

…which produces 7 diatonic chords audio output from 7 buttons. Only one button can be pressed at a time (no sound generated with multiple buttons pressed). The audio output is a 1-bit square wave.

C major > C E G
D min > D F A
E min > E G B
F major > F A C
G major > G B D
A min > A C E
B dim > B D F

  • The system clock input drives note divider logic
  • Internal counters divide the clock to generate square-wave note tones (7 notes of 7 different frequencies in total)
  • 3 notes are combined into a chord and outputted as audio
  • The system resets to no sound when the button is released

The HiChord has a multitude of other functions such as pitch bend, waveform selection, octave selection, etc. which were not implemented in my design.

I focused only on sound generation from 7 button inputs.

The resulting chip design meets the requirements of sky130hd and Tiny Tapeout and should be fabricatable.

7 Button Diatonic Chord Mono Synth ASIC > Design Logic

The chip design features:

  • 1x Clock INPUT
  • 1x Reset INPUT
  • 7x Button INPUTs
  • 1x Audio OUTPUT Register

  • A Prescaler Logic Block was written to reduce the assumed 50MHz input clock signal down to 1MHz to make it possible to play notes from the 3rd and 4th registers of a piano with frequencies ranging from 131.81 Hz (C3) to 987.77 Hz (B5).

  • A 16-bit Counter was divided and utilized to generate square waves of different frequencies from C3 to B3, 130.81 Hz to 246.94 Hz.

  • A 7-bit Signal Wire was created to accept INPUT signals from 7 different buttons, each assigned to a specific note.

  • 3 different Button Conditions were defined: exactly one pressed, more than one pressed and none pressed.

  • Verilog OR Reduction logic was applied to recognize which Button Condition is active.

  • A 16-bit register is utilized to store the 3 note values of the currently pressed chord key.

  • A long Conditional Statement is used to determine which key was pressed and which 3 notes should be sent to the Chord Register.

  • A 16-bit register is used to record counter and tone values.

  • Moore State Machine Logic is utilized generate and output chord audio.

7 Button Diatonic Chord Mono Synth ASIC > Verilog RTL Code

// 7-Button Diatonic Chord Monophonic Synthesizer Chip  
// coded with ChatGPT assistance  

module button_chord_7_optimized (
    input  clk,
    input  reset,

    //unique input for each of 7 chord button
    input  BTN_C,   // C major = C E G
    input  BTN_D,   // D minor = D F A
    input  BTN_E,   // E minor = E G B
    input  BTN_F,   // F major = F A C
    input  BTN_G,   // G major = G B D
    input  BTN_A,   // A minor = A C E
    input  BTN_B,   // B dim   = B D F

    output reg audio_out //assigned to always block 
);

    // Clock Prescaler: 50 MHz -> 1 MHz 
    reg [5:0] prescaler; //6-bit register 
    reg audio_tick; 

    // Prescaler Logic block 
    always @(posedge clk or posedge reset) begin
        if (reset) begin
            prescaler  <= 6'd0;
            audio_tick <= 1'b0;
        end else begin
            if (prescaler == 6'd49) begin //count to 50
                prescaler  <= 6'd0;
                audio_tick <= 1'b1; //generate one audio tick
            end else begin
                prescaler  <= prescaler + 6'd1;
                audio_tick <= 1'b0;
            end
        end
    end

    // Prescaled Clock
    // divider = 1,000,000 / (2 * f_note)  
    // generate 7 note frequencies
    localparam [15:0] C3_DIV = 16'd3822; // 130.81 Hz
    localparam [15:0] D3_DIV = 16'd3405; // 146.83 Hz
    localparam [15:0] E3_DIV = 16'd3034; // 164.81 Hz
    localparam [15:0] F3_DIV = 16'd2863; // 174.61 Hz
    localparam [15:0] G3_DIV = 16'd2551; // 196.00 Hz
    localparam [15:0] A3_DIV = 16'd2273; // 220.00 Hz
    localparam [15:0] B3_DIV = 16'd2025; // 246.94 Hz


    // Button handling
    // valid_chord = exactly one button is pressed
    wire [6:0] buttons; //7-bit signal line
    assign buttons = {BTN_C, BTN_D, BTN_E, BTN_F, BTN_G, BTN_A, BTN_B}; //assigns button names to each bit of the signal line (like an array) each to express a boolean value for pressed or unpressed 

    // define signal lines
    wire any_pressed;
    wire more_than_one;
    wire valid_chord;

    // button press checks
    assign any_pressed    = |buttons; //verilog 'reduction OR', "is any button pressed?"
    assign more_than_one  = |(buttons & (buttons - 7'd1)); //"is more than one button pressed"
    assign valid_chord    = any_pressed & ~more_than_one; //"is only one button pressed?"

    // Selected divider values for currently active chord
    reg [15:0] div1, div2, div3;

    // Chord Selection Logic Block
    // Chord case states for each button
    always @(*) begin
        // default = silence
        div1 = 16'd0;
        div2 = 16'd0;
        div3 = 16'd0;

        if (valid_chord) begin
            if (BTN_C) begin
                // C major = C E G
                div1 = C3_DIV;
                div2 = E3_DIV;
                div3 = G3_DIV;
            end
            else if (BTN_D) begin
                // D minor = D F A
                div1 = D3_DIV;
                div2 = F3_DIV;
                div3 = A3_DIV;
            end
            else if (BTN_E) begin
                // E minor = E G B
                div1 = E3_DIV;
                div2 = G3_DIV;
                div3 = B3_DIV;
            end
            else if (BTN_F) begin
                // F major = F A C
                div1 = F3_DIV;
                div2 = A3_DIV;
                div3 = C3_DIV;
            end
            else if (BTN_G) begin
                // G major = G B D
                div1 = G3_DIV;
                div2 = B3_DIV;
                div3 = D3_DIV;
            end
            else if (BTN_A) begin
                // A minor = A C E
                div1 = A3_DIV;
                div2 = C3_DIV;
                div3 = E3_DIV;
            end
            else begin
                // BTN_B
                // B diminished = B D F
                div1 = B3_DIV;
                div2 = D3_DIV;
                div3 = F3_DIV;
            end
        end
    end

    // Three independent tone generators
    reg [15:0] counter1, counter2, counter3;
    reg tone1, tone2, tone3;

    // Tone Generator Logic Block
    always @(posedge clk or posedge reset) begin
        if (reset) begin
            counter1  <= 16'd0;
            counter2  <= 16'd0;
            counter3  <= 16'd0;
            tone1     <= 1'b0;
            tone2     <= 1'b0;
            tone3     <= 1'b0;
            audio_out <= 1'b0;
        end else begin
            if (valid_chord) begin
                if (audio_tick) begin
                    // tone 1
                    if (counter1 >= div1) begin
                        counter1 <= 16'd0;
                        tone1    <= ~tone1;
                    end else begin
                        counter1 <= counter1 + 16'd1;
                    end

                    // tone 2
                    if (counter2 >= div2) begin
                        counter2 <= 16'd0;
                        tone2    <= ~tone2;
                    end else begin
                        counter2 <= counter2 + 16'd1;
                    end

                    // tone 3
                    if (counter3 >= div3) begin
                        counter3 <= 16'd0;
                        tone3    <= ~tone3;
                    end else begin
                        counter3 <= counter3 + 16'd1;
                    end

                    // Chord Output
                    audio_out <= tone1 ^ tone2 ^ tone3;
                end
            end else begin
                // silence if zero or multiple buttons are pressed
                counter1  <= 16'd0;
                counter2  <= 16'd0;
                counter3  <= 16'd0;
                tone1     <= 1'b0;
                tone2     <= 1'b0;
                tone3     <= 1'b0;
                audio_out <= 1'b0;
            end
        end
    end

endmodule

7 Button Diatonic Chord Mono Synth ASIC > Testbench Code

// 7 Button Chord Synth Testbench 
`timescale 1ns/1ps

module testbench;

reg clk;
reg reset;

reg BTN_C;
reg BTN_D;
reg BTN_E;
reg BTN_F;
reg BTN_G;
reg BTN_A;
reg BTN_B;

wire audio_out;

// Instantiate your synth
seven_button_chord_synth uut (
    .clk(clk),
    .reset(reset),
    .BTN_C(BTN_C),
    .BTN_D(BTN_D),
    .BTN_E(BTN_E),
    .BTN_F(BTN_F),
    .BTN_G(BTN_G),
    .BTN_A(BTN_A),
    .BTN_B(BTN_B),
    .audio_out(audio_out)
);

// clock generator
always #10 clk = ~clk;

initial begin

    $dumpfile("synth.vcd");   // waveform file
    $dumpvars(0,testbench);

    clk = 0;
    reset = 1;

    BTN_C=0; BTN_D=0; BTN_E=0;
    BTN_F=0; BTN_G=0; BTN_A=0; BTN_B=0;

    #50 reset = 0;

    // press C chord
    #100 BTN_C = 1;
    #200 BTN_C = 0;

    // press F chord
    #200 BTN_F = 1;
    #200 BTN_F = 0;

    // press G chord
    #200 BTN_G = 1;
    #200 BTN_G = 0;

    #500 $finish;

end

endmodule

7 Button Diatonic Chord Mono Synth ASIC > Production Workflow

The full toolchain necessary to process the Verilog RTL code to generate a fabricatable GDS file was installed in a Windows Subsystem Linux (WSL) Ubuntu 22.04 environment. The workflow and software utilized were as follows:

From design to ASIC fabrication:

  1. Conceptualize the Functionality of a Small Chip Design

    • Answer > What do I want the chip to do?
    • ex: “Blink and LED when a button is pushed”
    • Make a functionality flow diagram
  2. Write Verilog RTL Code

    • Describe the conceptual functionality as data-level (RTL) code descriptions
    • Use Icarus-Verilog
  3. Simulate the design

    • Test the RTL code in simulation to ensure the functionality works as expected
    • Use Icarus-Verilog
  4. View simulation waveforms

    • Review waveforms generated by the simulation to confirm correct functionality
    • Use GTKwave
  5. Synthesize RTL into Gate Level representation

    • Convert RTL code into a gate-level description of the hardware circuit
    • Use Yosys
  6. Place & Route connection

    • Define physical connections between gates and output GDS file
    • Use OpenRoad
  7. Inspect Layout

    • Review chip design physical layout for errors
    • Use KLayout
  8. Send GDS to Chip Foundry for Fabrication

Reports & Files

Reports:
- Floor Plan
- Detailed Place - Global Place - Resizer - CTS Final - Glogal Route - Route DRC - Finish

GDS:
- 7_button_chord_synth.gds

7 Button Diatonic Chord Mono Synth ASIC > Logic:

alt text

7 Button Diatonic Chord Mono Synth ASIC > Waveforms

alt text

7 Button Diatonic Chord Mono Synth ASIC > Features:

Chip Area: ~4800 sq. microns
Area Utilization: 77%

450 logic cells
59 Flip-Flops
11 Clocks
7 Inputs

Cells:
50% Sequential
8.8% Combinational
41.2% Clock

Power:
2.17 mW Power Req’t

0.954 mW Sequential
0.13 mW Combinational
1.09 mW Clock

Finish Critical Path Delay > 2.6057
Finish Critical Path Slack > 0.1244 Fmax > 420 MHz

7 Button Diatonic Chord Mono Synth ASIC > Chip Images

Final Placement
Final Placement

Final Routing
alt text

Final Clock
alt text

Final Worst Path
alt text

Final Congestion
alt text

Final IR Drop
alt text