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.