diff --git a/tutorials/soc/lesson8/Makefile b/tutorials/soc/lesson8/Makefile index 1b47413..474dc41 100644 --- a/tutorials/soc/lesson8/Makefile +++ b/tutorials/soc/lesson8/Makefile @@ -14,7 +14,7 @@ font.part: font.fnt dd if=$< of=$@ bs=1 count=768 skip=256 font.c: font.part bin2c - ./bin2c font.part font.c "unsigned char font[]" + ./bin2c font.part font.c "const unsigned char font[]" irqvec.rel: irqvec.s sdasz80 -o $@ $< @@ -59,5 +59,12 @@ run: $(ROM) sx $< <$(TTY) >$(TTY) echo "r" > $(TTY) +core_update: + stty -F $(TTY) speed 115200 raw -echo + timeout 1s cp $(TTY) /dev/null || /bin/true + echo "x core.rbf `stat -c%s soc.rbf`" > $(TTY) + sx soc.rbf <$(TTY) >$(TTY) + echo "r" > $(TTY) + reset: echo "r" > $(TTY) diff --git a/tutorials/soc/lesson8/boot_rom.c b/tutorials/soc/lesson8/boot_rom.c index ac95960..547436b 100644 --- a/tutorials/soc/lesson8/boot_rom.c +++ b/tutorials/soc/lesson8/boot_rom.c @@ -22,24 +22,40 @@ __sfr __at 0x11 PsgDataPort; // YM replay is happening in the interrupt void isr(void) __interrupt { - BYTE i; + BYTE i, *p; if(frames) { frames--; - // write all 16 psg registers - for(i=0;i<16;i++) { - PsgAddrPort = i; - PsgDataPort = ym_buffer[rsec][rptr++]; + // write all 14 psg sound registers + p = ym_buffer[rsec] + rptr; - // one whole sector processed? - if(rptr == 512) { - rsec++; - rptr=0; - - if(rsec == BUFFERS) - rsec = 0; - } + // unrolled loop for min delay between register writes + PsgAddrPort = 0; PsgDataPort = *p++; + PsgAddrPort = 1; PsgDataPort = *p++; + PsgAddrPort = 2; PsgDataPort = *p++; + PsgAddrPort = 3; PsgDataPort = *p++; + PsgAddrPort = 4; PsgDataPort = *p++; + PsgAddrPort = 5; PsgDataPort = *p++; + PsgAddrPort = 6; PsgDataPort = *p++; + PsgAddrPort = 7; PsgDataPort = *p++; + PsgAddrPort = 8; PsgDataPort = *p++; + PsgAddrPort = 9; PsgDataPort = *p++; + PsgAddrPort = 10; PsgDataPort = *p++; + PsgAddrPort = 11; PsgDataPort = *p++; + PsgAddrPort = 12; PsgDataPort = *p++; + PsgAddrPort = 13; + if(*p != 255) PsgDataPort = *p++; + + rptr += 16; + + // one whole sector processed? + if(rptr == 512) { + rsec++; + rptr=0; + + if(rsec == BUFFERS) + rsec = 0; } } else { // not playing? mute all channels @@ -127,7 +143,7 @@ void main() { FATFS fatfs; /* File system object */ FRESULT rc; UINT bytes_read; - BYTE wsec = 0; + BYTE i, wsec = 0; ei(); cls(); @@ -136,6 +152,12 @@ void main() { printf("Mounting SD card...\n"); + // not playing? mute all channels + for(i=0;i<16;i++) { + PsgAddrPort = i; + PsgDataPort = 0; + } + rc = pf_mount(&fatfs); if (rc) die(rc); diff --git a/tutorials/soc/lesson8/sigma_delta_dac.v b/tutorials/soc/lesson8/sigma_delta_dac.v index 5eb8049..b0085de 100644 --- a/tutorials/soc/lesson8/sigma_delta_dac.v +++ b/tutorials/soc/lesson8/sigma_delta_dac.v @@ -1,32 +1,129 @@ -module sigma_delta_dac( - output reg DACout, //Average Output feeding analog lowpass - input [MSBI:0] DACin, //DAC input (excess 2**MSBI) - input CLK, - input RESET +// sigmadelta.v +// two channel second order sigma delta dac +// taken from Minimig + +// audio data processing +// stereo sigma/delta bitstream modulator +module sigma_delta_dac ( + input clk, // bus clock + input [14:0] ldatasum, // left channel data + input [14:0] rdatasum, // right channel data + output reg left=0, // left bitstream output + output reg right=0 // right bitsteam output ); -parameter MSBI = 7; +//-------------------------------------------------------------------------------------- -reg [MSBI+2:0] DeltaAdder; //Output of Delta Adder -reg [MSBI+2:0] SigmaAdder; //Output of Sigma Adder -reg [MSBI+2:0] SigmaLatch; //Latches output of Sigma Adder -reg [MSBI+2:0] DeltaB; //B input of Delta Adder +// local signals +localparam DW = 15; +localparam CW = 2; +localparam RW = 4; +localparam A1W = 2; +localparam A2W = 5; -always @ (*) - DeltaB = {SigmaLatch[MSBI+2], SigmaLatch[MSBI+2]} << (MSBI+1); +wire [DW+2+0 -1:0] sd_l_er0, sd_r_er0; +reg [DW+2+0 -1:0] sd_l_er0_prev=0, sd_r_er0_prev=0; +wire [DW+A1W+2-1:0] sd_l_aca1, sd_r_aca1; +wire [DW+A2W+2-1:0] sd_l_aca2, sd_r_aca2; +reg [DW+A1W+2-1:0] sd_l_ac1=0, sd_r_ac1=0; +reg [DW+A2W+2-1:0] sd_l_ac2=0, sd_r_ac2=0; +wire [DW+A2W+3-1:0] sd_l_quant, sd_r_quant; -always @(*) - DeltaAdder = DACin + DeltaB; - -always @(*) - SigmaAdder = DeltaAdder + SigmaLatch; - -always @(posedge CLK or posedge RESET) - if(RESET) begin - SigmaLatch <= 1'b1 << (MSBI+1); - DACout <= 1'b0; - end else begin - SigmaLatch <= SigmaAdder; - DACout <= SigmaLatch[MSBI+2]; - end -endmodule \ No newline at end of file +// LPF noise LFSR +reg [24-1:0] seed1 = 24'h654321; +reg [19-1:0] seed2 = 19'h12345; +reg [24-1:0] seed_sum=0, seed_prev=0, seed_out=0; +always @ (posedge clk) begin + if (&seed1) + seed1 <= #1 24'h654321; + else + seed1 <= #1 {seed1[22:0], ~(seed1[23] ^ seed1[22] ^ seed1[21] ^ seed1[16])}; +end +always @ (posedge clk) begin + if (&seed2) + seed2 <= #1 19'h12345; + else + seed2 <= #1 {seed2[17:0], ~(seed2[18] ^ seed2[17] ^ seed2[16] ^ seed2[13] ^ seed2[0])}; +end +always @ (posedge clk) begin + seed_sum <= #1 seed1 + {5'b0, seed2}; + seed_prev <= #1 seed_sum; + seed_out <= #1 seed_sum - seed_prev; +end + +// linear interpolate +localparam ID=4; // counter size, also 2^ID = interpolation rate +reg [ID+0-1:0] int_cnt = 0; +always @ (posedge clk) int_cnt <= #1 int_cnt + 'd1; + +reg [DW+0-1:0] ldata_cur=0, ldata_prev=0; +reg [DW+0-1:0] rdata_cur=0, rdata_prev=0; +wire [DW+1-1:0] ldata_step, rdata_step; +reg [DW+ID-1:0] ldata_int=0, rdata_int=0; +wire [DW+0-1:0] ldata_int_out, rdata_int_out; +assign ldata_step = {ldata_cur[DW-1], ldata_cur} - {ldata_prev[DW-1], ldata_prev}; // signed subtract +assign rdata_step = {rdata_cur[DW-1], rdata_cur} - {rdata_prev[DW-1], rdata_prev}; // signed subtract +always @ (posedge clk) begin + if (~|int_cnt) begin + ldata_prev <= #1 ldata_cur; + ldata_cur <= #1 ldatasum; //{~ldatasum[DW-1], ldatasum[DW-2:0]}; // convert to offset binary, samples no longer signed! + rdata_prev <= #1 rdata_cur; + rdata_cur <= #1 rdatasum; //{~rdatasum[DW-1], rdatasum[DW-2:0]}; // convert to offset binary, samples no longer signed! + ldata_int <= #1 {ldata_cur[DW-1], ldata_cur, {ID{1'b0}}}; + rdata_int <= #1 {rdata_cur[DW-1], rdata_cur, {ID{1'b0}}}; + end else begin + ldata_int <= #1 ldata_int + {{ID{ldata_step[DW+1-1]}}, ldata_step}; + rdata_int <= #1 rdata_int + {{ID{rdata_step[DW+1-1]}}, rdata_step}; + end +end +assign ldata_int_out = ldata_int[DW+ID-1:ID]; +assign rdata_int_out = rdata_int[DW+ID-1:ID]; + +// input gain x3 +wire [DW+2-1:0] ldata_gain, rdata_gain; +assign ldata_gain = {ldata_int_out[DW-1], ldata_int_out, 1'b0} + {{(2){ldata_int_out[DW-1]}}, ldata_int_out}; +assign rdata_gain = {rdata_int_out[DW-1], rdata_int_out, 1'b0} + {{(2){rdata_int_out[DW-1]}}, rdata_int_out}; + +/* +// random dither to 15 bits +reg [DW-1:0] ldata=0, rdata=0; +always @ (posedge clk) begin + ldata <= #1 ldata_gain[DW+2-1:2] + ( (~(&ldata_gain[DW+2-1-1:2]) && (ldata_gain[1:0] > seed_out[1:0])) ? 15'd1 : 15'd0 ); + rdata <= #1 rdata_gain[DW+2-1:2] + ( (~(&ldata_gain[DW+2-1-1:2]) && (ldata_gain[1:0] > seed_out[1:0])) ? 15'd1 : 15'd0 ); +end +*/ + +// accumulator adders +assign sd_l_aca1 = {{(A1W){ldata_gain[DW+2-1]}}, ldata_gain} - {{(A1W){sd_l_er0[DW+2-1]}}, sd_l_er0} + sd_l_ac1; +assign sd_r_aca1 = {{(A1W){rdata_gain[DW+2-1]}}, rdata_gain} - {{(A1W){sd_r_er0[DW+2-1]}}, sd_r_er0} + sd_r_ac1; + +assign sd_l_aca2 = {{(A2W-A1W){sd_l_aca1[DW+A1W+2-1]}}, sd_l_aca1} - {{(A2W){sd_l_er0[DW+2-1]}}, sd_l_er0} - {{(A2W+1){sd_l_er0_prev[DW+2-1]}}, sd_l_er0_prev[DW+2-1:1]} + sd_l_ac2; +assign sd_r_aca2 = {{(A2W-A1W){sd_r_aca1[DW+A1W+2-1]}}, sd_r_aca1} - {{(A2W){sd_r_er0[DW+2-1]}}, sd_r_er0} - {{(A2W+1){sd_r_er0_prev[DW+2-1]}}, sd_r_er0_prev[DW+2-1:1]} + sd_r_ac2; + +// accumulators +always @ (posedge clk) begin + sd_l_ac1 <= #1 sd_l_aca1; + sd_r_ac1 <= #1 sd_r_aca1; + sd_l_ac2 <= #1 sd_l_aca2; + sd_r_ac2 <= #1 sd_r_aca2; +end + +// value for quantizaton +assign sd_l_quant = {sd_l_ac2[DW+A2W+2-1], sd_l_ac2} + {{(DW+A2W+3-RW){seed_out[RW-1]}}, seed_out[RW-1:0]}; +assign sd_r_quant = {sd_r_ac2[DW+A2W+2-1], sd_r_ac2} + {{(DW+A2W+3-RW){seed_out[RW-1]}}, seed_out[RW-1:0]}; + +// error feedback +assign sd_l_er0 = sd_l_quant[DW+A2W+3-1] ? {1'b1, {(DW+2-1){1'b0}}} : {1'b0, {(DW+2-1){1'b1}}}; +assign sd_r_er0 = sd_r_quant[DW+A2W+3-1] ? {1'b1, {(DW+2-1){1'b0}}} : {1'b0, {(DW+2-1){1'b1}}}; +always @ (posedge clk) begin + sd_l_er0_prev <= #1 (&sd_l_er0) ? sd_l_er0 : sd_l_er0+1; + sd_r_er0_prev <= #1 (&sd_r_er0) ? sd_r_er0 : sd_r_er0+1; +end + +// output +always @ (posedge clk) begin + left <= #1 (~|ldata_gain) ? ~left : ~sd_l_er0[DW+2-1]; + right <= #1 (~|rdata_gain) ? ~right : ~sd_r_er0[DW+2-1]; +end + +endmodule diff --git a/tutorials/soc/lesson8/soc.rbf b/tutorials/soc/lesson8/soc.rbf index 04e3077..7b3ad10 100644 Binary files a/tutorials/soc/lesson8/soc.rbf and b/tutorials/soc/lesson8/soc.rbf differ diff --git a/tutorials/soc/lesson8/soc.v b/tutorials/soc/lesson8/soc.v index 9cc8d57..73dfd2a 100644 --- a/tutorials/soc/lesson8/soc.v +++ b/tutorials/soc/lesson8/soc.v @@ -260,15 +260,15 @@ spi spi ( // Audio replay is supposed to run at 50Hz. Vsync is 60 Hz, so // we generate. reg clk50hz; -reg [16:0] count_50hz; +reg [15:0] count_50hz; always @(posedge cpu_clock) begin if(cpu_reset) - count_50hz <= 17'd0; + count_50hz <= 16'd0; else begin - if(count_50hz < 16'd159999) - count_50hz <= count_50hz + 17'd1; + if(count_50hz < 16'd39999) + count_50hz <= count_50hz + 16'd1; else begin - count_50hz <= 17'd0; + count_50hz <= 16'd0; clk50hz <= !clk50hz; end end @@ -343,15 +343,14 @@ YM2149 ym2149 ( .CLK8 ( cpu_clock ) // 4 MHz CPU bus clock ); -assign AUDIO_L = audio_out; -assign AUDIO_R = audio_out; -wire audio_out; +wire [14:0] audio_data = { psg_audio_out, 7'h00 } - 15'h4000; sigma_delta_dac sigma_delta_dac ( - .DACout ( audio_out ), - .DACin ( psg_audio_out ), - .CLK ( ram_clock ), - .RESET ( cpu_reset ) + .clk ( ram_clock ), + .left ( AUDIO_L ), + .right ( AUDIO_R ), + .ldatasum ( audio_data ), + .rdatasum ( audio_data ) ); // divide 32Mhz sdram clock down to 2MHz diff --git a/tutorials/soc/lesson8/z80_soc.rom b/tutorials/soc/lesson8/z80_soc.rom index 99dd2d2..9c68265 100644 Binary files a/tutorials/soc/lesson8/z80_soc.rom and b/tutorials/soc/lesson8/z80_soc.rom differ