diff options
author | H. Peter Anvin <hpa@zytor.com> | 2005-01-31 18:01:40 +0000 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2005-01-31 18:01:40 +0000 |
commit | 45ce78ae8db253547475f0e344f224e3b8b0defc (patch) | |
tree | ac0291eb20b6dfcbe32f8993b56b7ed6d6e11a45 | |
parent | 362d3b89e4f5f69e07ef56c39b63d0396172ef10 (diff) | |
download | abc80-45ce78ae8db253547475f0e344f224e3b8b0defc.tar.gz abc80-45ce78ae8db253547475f0e344f224e3b8b0defc.tar.xz abc80-45ce78ae8db253547475f0e344f224e3b8b0defc.zip |
Actual sound engine
-rw-r--r-- | sound.v | 328 |
1 files changed, 328 insertions, 0 deletions
@@ -0,0 +1,328 @@ +// $Id$ +// +// Sound controller for ABC80 +// +// This attempts to simulate the SN76477 sound generator +// *as used in ABC80*. +// +// ABC80 has the following (approximate) parameters: +// +// SLF = 2.9 Hz +// VCOmin = 640 Hz +// Noise cutoff = 10 kHz +// Attack = 22 ms +// Decay = 470 ms + + +// Model the 76477 SLF. This returns a "sawtooth" value +// between (approximately) [1024,12288) which gives about +// the 10:1 range needed by the VCO. +`define vco_min 14'd1024 +`define vco_max 14'd12288 + +module slf( + input clk, // 25 MHz + input clk_en, // One pulse every 16 us (62.5 kHz) + output slf, // SLF squarewave + output [13:0] saw // Sawtooth magnitude + ); + reg up = 1; + reg [13:0] ctr = `vco_min; + + assign slf = up; + assign saw = ctr; + + always @(posedge clk) + if ( clk_en ) + begin + if ( ctr[13:10] == 4'b1100 ) + up <= 0; + else if ( ctr[13:10] == 4'b0000 ) + up <= 1; + + if ( up ) + ctr <= ctr + 1; + else + ctr <= ctr - 1; + end // if ( clk_en ) +endmodule // slf + +// +// The VCO. The output frequency = clk/pitch/2. +// +module vco( + input clk, // 25 MHz + input [13:0] pitch, // Pitch control + output vco, // VCO squarewave output + output vco2 // VCO output with every other pulse suppressed + ); + reg [14:0] ctr = 0; + reg [1:0] cycle; + + assign vco = cycle[0]; + assign vco2 = cycle[0] & cycle[1]; + + always @(posedge clk) + begin + if ( ctr == 0 ) + begin + ctr <= { pitch, 1'b0 }; + cycle <= cycle + 1; + end + else + ctr <= ctr - 1; + end // always @ (posedge clk) +endmodule // vco + +// +// Noise (e.g. random number) generator./ +// +module noise( + input clk, // 25 MHz + input clk_en, // One pulse every 16 us (62.5 kHz) + output noise + ); + reg [15:0] lfsr = ~16'h0; // Must be nonzero + + assign noise = lfsr[15]; + + wire lfsr_zero = (lfsr == 0); + + always @(posedge clk) + if ( clk_en ) + lfsr <= { lfsr[14:0], lfsr_zero } ^ (lfsr[15] ? 16'h54b9 : 16'h0); +endmodule // noise + +// +// Mixer +// +module mixer( + input slf, + input vco, + input noise, + input [2:0] mixer_ctl, + output mixer_out + ); + reg out; + + assign mixer_out = out; + + always @(*) + case ( mixer_ctl ) + 3'b000: + out <= vco; + 3'b001: + out <= slf; + 3'b010: + out <= noise; + 3'b011: + out <= vco & noise; + 3'b100: + out <= slf & noise; + 3'b101: + out <= slf & vco & noise; + 3'b110: + out <= slf & vco; + 3'b111: + out <= 1; + endcase // case( mixer_ctl ) +endmodule // mixer + +// +// Envelope generator, consisting of one-shot generator, +// envelope select, and envelope generation (attack/decay.) +// Output is parallel digital. +// +module oneshot( + input clk, // 25 MHz + input clk_en, // One pulse every 16 us (62.5 kHz) + input inhibit, + output oneshot + ); + reg out = 0; + reg inhibit1 = 0; + reg [10:0] ctr = 0; + + reg oneshot; + + wire ctr_or = |ctr; + + always @(posedge clk) + if ( clk_en ) + begin + inhibit1 <= inhibit; + oneshot <= ctr_or; + + if ( ~inhibit & inhibit1 ) + ctr <= 11'd1624; // ~26 ms + else if ( ctr_or ) + ctr <= ctr - 1; + end +endmodule // oneshot + +module envelope_select( + input [1:0] envsel, + input oneshot, + input vco, + input vco2, + output envelope + ); + reg envelope; + + always @(*) + begin + case ( envsel ) + 2'b00: + envelope <= vco; + 2'b01: + envelope <= 1; + 2'b10: + envelope <= oneshot; + 2'b11: + envelope <= vco2; + endcase // case( envsel ) + end // always @ (*) +endmodule // envelope_select + +module envelope_shape( + input clk, // 25 MHz + input clk_en, // One pulse every 16 us (62.5 kHz) + input envelope, + output [13:0] env_mag + ); + reg [13:0] env_mag = 0; + + always @(posedge clk) + if ( clk_en ) + begin + if ( envelope ) + begin + if ( env_mag[13:11] != 3'b111 ) + env_mag <= env_mag + 20; + end + else + begin + if ( |env_mag ) + env_mag <= env_mag - 1; + end + end // if ( clk_en ) +endmodule // envelope_shape + +// +// Delta-sigma DAC for the final output +// +module dac( + input [13:0] magnitude, + input clk, // The higher the better + output signal + ); + reg [13:0] mag_latch = 0; + reg [15:0] sigma_latch = 0; + + assign signal = sigma_latch[15]; + + wire [15:0] delta_add = { sigma_latch[15], sigma_latch[15], mag_latch }; + wire [15:0] sigma_add = delta_add + sigma_latch; + + + always @(posedge clk) + begin + mag_latch <= magnitude; + sigma_latch <= sigma_add; + end +endmodule // dac + +// +// Putting it all together... +// +module sound_generator( + input ctr_16us, + input clk_fast, + input clk_25MHz, + + input [2:0] mixer_ctl, + input vco_sel, + input vco_pitch, + input [1:0] envsel, + input inhibit, + + output signal, + output [7:0] debug + ); + wire w_slf; + wire [13:0] saw; + wire [13:0] vco_level; + wire w_vco; + wire w_vco2; + wire w_envelope; + wire w_noise; + wire w_oneshot; + wire w_mixer_out; + + wire [13:0] env_mag; + wire signal_on; + wire [13:0] magnitude; + + reg ctr_16us_1; + reg clk_en; + + // ctr_16us should be synchronous with clk_25MHz. + // We set clk_en to 1 for one cycle every positive edge of ctr_16us. + + always @(posedge clk_25MHz) + begin + ctr_16us_1 <= ctr_16us; + clk_en <= ctr_16us & ~ctr_16us_1; + end + + slf slf ( .clk (clk_25MHz), + .clk_en (clk_en), + .saw (saw), + .slf (w_slf) ); + + assign vco_level = vco_sel ? saw : vco_pitch ? `vco_max : `vco_min; + + vco vco ( .clk (clk_25MHz), + .pitch (vco_level), + .vco (w_vco), + .vco2 (w_vco2) ); + + noise noise ( .clk (clk_25MHz), + .clk_en (clk_en), + .noise (w_noise) ); + + + mixer mixer ( .slf (w_slf), + .vco (w_vco), + .noise (w_noise), + .mixer_ctl (mixer_ctl), + .mixer_out (w_mixer_out) ); + + + oneshot oneshot ( .clk (clk_25MHz), + .clk_en (clk_en), + .inhibit (inhibit), + .oneshot (w_oneshot) ); + + envelope_select envelope_select ( .envsel (envsel), + .oneshot (w_oneshot), + .vco (w_vco), + .vco2 (w_vco2), + .envelope (w_envelope) ); + + envelope_shape envelope_shape ( .clk (clk_25MHz), + .clk_en (clk_en), + .envelope (w_envelope), + .env_mag (env_mag) ); + + + assign signal_on = ~inhibit & w_mixer_out; + assign magnitude = signal_on ? env_mag : 0; + + dac dac ( .magnitude (magnitude), + .clk (clk_fast), + .signal (signal) ); + + assign debug = { w_noise, w_slf, w_vco, w_vco2, w_oneshot, w_envelope, w_mixer_out, signal_on }; + +endmodule // sound_generator |