Pull to refresh

Music box on Verilog HDL for Cyclone IV E

Reading time4 min
Views1.6K

Authors

Demid Efremov and Ivan Kornienko.

Introduction

Playing music on a device like Cyclone IV could be a tricky task and an interesting project to learn more about it. We decided to tackle this topic by creating simple and expandable music box for Cyclone IV E which is easy to write music for.

Hardware and software used

Our project is implemented on Cyclone IV FPGA Device using Quartus Prime Lite 20.1 on Verilog HDL. To recreate this project an Altera USB Blaster is also required to program the board.

Implementation

Concept

The concept of our music box resembles a real-life music box with rolls of notes and duration. As note is being played, timer counts for number of ticks until it owerflows specified note duration for both note and duration rolls to be shifted to the next note.

Clock dividers

We found out that stock 10MHz clock of Cyclone IV is not very useful for generating music. We decided to make a clock division mechanism which is capable of changing clocks both for note tone and duration. Changing rate of this dividers allows for dynamic change of both playback speed and notes tone (though we decided to disable tone change by default).

always@(posedge clk or negedge rst)
begin
  if(!rst) begin
    clk_tune<=0;
    clk_dur <=0;
    end
  else begin
    if(clk_tune<ctrl[7:4])
      clk_tune<=clk_tune+1;
    else
      clk_tune<=0;
      
    if(clk_dur<ctrl[3:0])
      clk_dur<=clk_dur+1;
    else
      clk_dur<=0;
    end
end

Note duration switcher

At each tick of note duration clock divider a note duration counter gets increased and if it overflows a duration specified for note currently being played, note duration register get shifted and next duration is read.

always@(posedge clk or negedge rst)
begin  
  if(!rst) begin
    cnt<=0;
    note_dur<=dur_roll;Ц
  end
  else if(clk_dur>=ctrl[3:0]) begin
    if(cnt[31 : 16]!=note_dur[15 : 0]) begin
      cnt<=cnt+1;
      note_dur<=note_dur;
    end else begin
      note_dur=note_dur >> 16;
      cnt<=0;
    end
  end
end

Note tone switcher

Shifting of note tone is implemented separatly, in case we would want to be able to change the note tone manually. It is triggered at the same time duration roll gets shifted.

always@(posedge clk or negedge rst)
begin  
  if(!rst) begin
    note_tune<=tune_roll; 
  end 
  else if(clk_dur>=ctrl[3:0]) if(cnt[31 : 16]==note_dur[15 : 0]) begin
    note_tune=note_tune >> 16;
    debug<=note_tune[7 : 0];
  end else note_tune<=note_tune;
end

Output signal generator

To generate output signal, another counter, tone counter, is used. It counts on each tick of tone clock divider. If this counter overflows note tone of the current note, then counter gets reset and output signal for buzzer gets inverted, generating a square wave in which duration of each wave is specified in ticks of tone clock divider.

always@(posedge clk or negedge rst)
begin  
  if(!rst) tune<=0; else
  if(note_tune[15 : 0] == 0) out<=1; else
  if(cnt[31 : 16]==note_dur[15 : 0])tune<=0;else
  if(clk_tune>=ctrl[7:4]) begin
    if(tune!=note_tune[15 : 0]) begin
      tune<=tune+1;
      out<=out;
    end else begin
      tune<=0;
      out<=~out;
    end
  end
end

All the code with some additional comments

module MusicBox(clk,rst,out,debug,control);
input  clk,rst;
input [7 : 0] control;
output out;
output[7 : 0] debug;
reg out;
reg [7 : 0] debug;

//note tone defenitions
//тон нот:
parameter dA = 16'd4584; 
parameter C = 16'd3822;
parameter D = 16'd3405;
parameter E = 16'd3034;
parameter F = 16'd2865;
parameter G = 16'd2551;
parameter uA = 16'd2273;
parameter Bb = 16'd2148;

//note duration defenitions
//длительность нот:
parameter q = 16'd24;
parameter q1 = 16'd48;
parameter p = 16'h0;

reg[31 : 0] cnt;
reg[15 : 0] tune;
reg[15 : 0] clk_tune;
reg[7  : 0] clk_dur;


//melody (separate rolls for duration and tone) - read BACKWARDS.
//мелодия (отдельные доржки длительности ноты и тона) - играется ЗАДОМ НАПЕРЕД.
reg[2047 : 0] tune_roll = {
D,C,D,p,E,p,E,F,D,p,D,uA,p,G,p,F,p,F,E,D,
p,D,uA,G,uA,p,Bb,p,Bb,uA,G,p,G,p,G,F,D,p,D,p,D,C,dA,
p,D,C,C,D,p,E,p,E,G,F,p,F,p,F,E,D,p,D,p,D,C,dA,
p,D,C,C,D,p,E,p,E,G,F,p,F,p,F,E,D,p,D,p,D,C,dA
};

reg[2047 : 0]  dur_roll = {
q,q,q,q,q,q,q,q,q,q1,q,q,q,q,q,q,q,q,q,q,
q1,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,
q1,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,
q1,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q
};

reg [7:0] ctrl;

reg[2047 : 0] note_tune;
reg[2047 : 0] note_dur;

//control of clock dividers (switch of octave and speed)
//управление делителем частоты (переключение октавы и скорости)
always@(control)
begin
	ctrl[7:4] <= 4'd4; //octave switch is disabled by default | переключение октав отключено, ctrl[7:4] <= ~control[3:0]; включит его через свитчи 5-8
	case (control[7:4])
	4'b1110: ctrl[3:0] <= 4'd3;
	4'b1101: ctrl[3:0] <= 4'd4;
	4'b1011: ctrl[3:0] <= 4'd6;
	4'b0111: ctrl[3:0] <= 4'd12;
	default: ctrl[3:0] <= 4'd4;
	endcase
end


//clock divider
//делитель частоты
always@(posedge clk or negedge rst)
begin
  if(!rst) begin
    clk_tune<=0;
    clk_dur <=0;
    end
  else begin
    if(clk_tune<ctrl[7:4])
      clk_tune<=clk_tune+1;
    else
      clk_tune<=0;
      
    if(clk_dur<ctrl[3:0])
      clk_dur<=clk_dur+1;
    else
      clk_dur<=0;
    end
end

//note duration switcher
//переключатель длительности ноты
always@(posedge clk or negedge rst)
begin  
  if(!rst) begin
    cnt<=0;
    note_dur<=dur_roll;Ц
  end
  else if(clk_dur>=ctrl[3:0]) begin
    if(cnt[31 : 16]!=note_dur[15 : 0]) begin
      cnt<=cnt+1;
      note_dur<=note_dur;
    end else begin
      note_dur=note_dur >> 16;
      cnt<=0;
    end
  end
end


//note tone switcher
//переключатель тона ноты, лампочки на плате показывают текущую ноту.
always@(posedge clk or negedge rst)
begin  
  if(!rst) begin
    note_tune<=tune_roll; 
  end 
  else if(clk_dur>=ctrl[3:0]) if(cnt[31 : 16]==note_dur[15 : 0]) begin
    note_tune=note_tune >> 16;
    debug<=note_tune[7 : 0];
  end else note_tune<=note_tune;
end

//output controller
//генератор выходного сигнала
always@(posedge clk or negedge rst)
begin  
  if(!rst) tune<=0; else
  if(note_tune[15 : 0] == 0) out<=1; else
  if(cnt[31 : 16]==note_dur[15 : 0])tune<=0;else
  if(clk_tune>=ctrl[7:4]) begin
    if(tune!=note_tune[15 : 0]) begin
      tune<=tune+1;
      out<=out;
    end else begin
      tune<=0;
      out<=~out;
    end
  end
end

endmodule

Conclusion

This little project allowed us to understand some quirks of Verilog HDL and to combine knowledge of computer arhitecture and music on practice.

Huge thanks to Artem Burmyakov, Alexander Tormasov and Vlad Ostankovich for intial knowledge required to work on this project and for lending an FPGA board for us.

Links

[1] This project on GitHub

Tags:
Hubs:
Total votes 3: ↑3 and ↓0+3
Comments0

Articles