/********************************************************************************* * * SID oscillator hardware emulation * * Just messing around with what's described in the intervew here: * http://sid.kubarth.com/articles/interview_bob_yannes.html * * Code not to be copied without giving adequate props of the "hella mad" variety. * **********************************************************************************/ module SID( input [15:0] FREQ, //Frequency control - accumulator increment value output wire [11:0] TRI, //Triangle wave output output wire [11:0] SAW, //Saw wave output output wire [11:0] TRI_AND_SAW,//Saw and Triangle wave combined output output wire [11:0] PULSE, //Pulse wave output output wire [11:0] NOISE, //Noiser wave output input CLK, //Master clock input input RES, //Master reset input [11:0] DUTY //Pulse wave duty cycle input ); wire [23:0] accum_out; accum my_accum( //Accumulator instantiation .CLK (CLK), .INC_VAL (FREQ), .OUT_VAL (accum_out), .RES (RES) ); assign SAW = accum_out[23:12]; //"upper 12-bits of the accumulator" assign TRI = {{11{accum_out[23]}}^(accum_out[22:12]), 1'b0}; //"using the MSB of the accumulator to invert the remaining upper 11 accumulator bits //using EXOR gates. These 11 bits were then left-shifted (throwing away the MSB)" assign TRI_AND_SAW = SAW & TRI; //"combination was actually a logical ANDing of the bits of each waveform" wire comparator = (accum_out[23:12]>=DUTY); //"12-bit digital comparator" assign PULSE = {12{comparator}}; //"single output... to all 12 bits" LFSR m_lfsr( .CLK (accum_out[16]), //"clocked by one of the intermediate bits of the accumulator" //I'm not sure which intermediate bit he used, so I just grabbed one that made for //pretty graphs. .RES (RES), .NOISE_OUT (NOISE) ); endmodule //Linear feedback shift register is put into separate module module LFSR( input CLK, input RES, output wire [11:0] NOISE_OUT ); reg [22:0] lfsr; //"23-bit pseudo-random sequence generator..." //"...a shift register with specific outputs fed back to the input through combinatorial logic" wire feedback = lfsr[22] ^ lfsr[17]; //Interview doesn't say which taps are used in the SID. These are grabbed from a Xilinx //app note on linear feedback shift registers. assign NOISE_OUT = lfsr[22:11]; //"The upper 12-bits of the shift register" always @(posedge CLK or posedge RES) if(RES) lfsr <= 23'd1; //lfsr must never contain all zeros else lfsr <= (lfsr<<1) + feedback; //shift! endmodule //Accumulator is in separate module to act as a wrapper. In a Spartan 6, for example, //this could be done in a DSP48A1 block. module accum( output reg [23:0] OUT_VAL, //"24-bit phase-accumulating design" input [15:0] INC_VAL, //"of which the lower 16-bits are programmable for pitch control" input CLK, input RES ); always @(posedge CLK) if(RES) OUT_VAL <= 'b0; //Reset Accumulator else OUT_VAL <= OUT_VAL + INC_VAL; //Increment Accumulator endmodule // Copyright 2010 - Analog Bytes, LLC