Skip to content
Go back
Hookpad → SystemVerilog: Programming Music on the Nexys A7

Hookpad → SystemVerilog: Programming Music on the Nexys A7

By Michael Earls

Edit page

Programming music directly into an FPGA can feel like a grind—especially if you’re hand-coding note tables and timing values. When working with the Digilent Nexys A7 (100 MHz), I wanted a more direct way to take melodies I’d written and make the board “sing” without endless trial and error.

That’s where Hookpad comes in. Hookpad is a web-based music sketchpad created by Hooktheory. It lets you build chord progressions and melodies quickly, and then export them in a structured JSON format. This JSON contains all the information about notes, durations, and rests—perfect raw material for generating Verilog code.


The Hookpad → SystemVerilog Converter

To streamline the workflow, I built a browser-based converter that takes a Hookpad JSON file and produces two ready-to-synthesize files:

  1. ROM module – stores note timing and frequency dividers.
  2. Top player module – instantiates the ROM and drives the AUD_PWM / AUD_SD pins on the Nexys A7.

👉 Try the tool here: /hookpad-to-verilog


How it works

  1. Load a Hookpad JSON file
    You can upload the file directly or paste its contents into the converter.
    The tool parses the note arrays and normalizes beats, rests, and durations.

  2. Build entries
    Each note is converted into:

    • div_val — a clock divider to generate the square wave frequency.
    • dur_ticks — the number of 100 MHz clock cycles for the note’s duration.
  3. Generate SystemVerilog modules

    • A ROM with a case statement containing all entries.
    • A top player that steps through the ROM and toggles the audio output.

Example ROM Module

// Auto-generated monophonic ROM from Hookpad JSON
module hookpad_rom_song(
  input  wire        clk,
  input  wire [11:0] addr,
  output reg  [31:0] div_val,
  output reg  [31:0] dur_ticks,
  output wire        valid
);
  localparam integer NOTE_COUNT = 4;
  assign valid = 1'b1;
  wire [11:0] addr_eff = addr;

  always @(posedge clk) begin
    case (addr_eff)
      12'd0: begin div_val <= 32'd191113; dur_ticks <= 32'd10000000; end
      12'd1: begin div_val <= 32'd170262; dur_ticks <= 32'd10000000; end
      12'd2: begin div_val <= 32'd151686; dur_ticks <= 32'd10000000; end
      12'd3: begin div_val <= 32'd143173; dur_ticks <= 32'd10000000; end
      default: begin div_val <= 32'd0; dur_ticks <= 32'd10_000_000; end
    endcase
  end
endmodule

Example Top Module (ROM Player)

// Drop-in monophonic top for Nexys A7 (100 MHz)
module top_hookpad_rom_song_mono #(
    parameter integer CLK_HZ = 100_000_000
)(
    input  wire CLK100MHZ,
    output wire AUD_SD,
    output reg  AUD_PWM
);
    assign AUD_SD = 1'b1; // enable amp

    // ROM instance
    wire [31:0] div_val;
    wire [31:0] dur_ticks;
    reg  [11:0] rom_addr = 12'd0;

    hookpad_rom_song rom_i (
        .clk       (CLK100MHZ),
        .addr      (rom_addr),
        .div_val   (div_val),
        .dur_ticks (dur_ticks),
        .valid     ()
    );

    // Tone generator
    reg [31:0] cnt = 0;
    reg        tone_hi = 0;
    reg [31:0] dur_cnt = 0;

    always @(posedge CLK100MHZ) begin
        if (dur_cnt >= dur_ticks) begin
            dur_cnt  <= 0;
            rom_addr <= rom_addr + 1;
            cnt      <= 0;
            tone_hi  <= 0;
        end else begin
            dur_cnt <= dur_cnt + 1;
        end

        if (div_val == 0) begin
            cnt     <= 0;
            tone_hi <= 0;
        end else if (cnt >= div_val) begin
            cnt     <= 0;
            tone_hi <= ~tone_hi;
        end else begin
            cnt <= cnt + 1;
        end

        AUD_PWM <= tone_hi;
    end
endmodule

How to use the converter

  1. Go to the converter page.
  2. Upload your Hookpad JSON file or paste the JSON.
  3. Enter the tempo, key, and mode.
  4. Click Generate Verilog.
  5. Download both the ROM and TOP .sv files.
  6. Add them to your Vivado project, mark them as SystemVerilog, and connect AUD_PWM / AUD_SD in the Nexys A7 XDC constraints.
  7. Synthesize, program, and listen!

Edit page
Share this post on:

Previous Post
Implementing the SAP-1 Computer on FPGA
Next Post
Exploring AI, Machine Learning, and Creative Projects