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.