From 7cc9ee807ddf9739e4db5769b8875bc8906a9826 Mon Sep 17 00:00:00 2001 From: slederer Date: Sat, 4 Oct 2025 00:09:10 +0200 Subject: [PATCH 01/11] tdraudio: remove pulse/noise waves, add sample buffer and irq --- tridoracpu/tridoracpu.srcs/irqctrl.v | 2 +- tridoracpu/tridoracpu.srcs/tdraudio.v | 167 ++++++++++++-------------- tridoracpu/tridoracpu.srcs/top.v | 22 ++-- tridoracpu/tridoracpu.xpr | 14 +-- 4 files changed, 98 insertions(+), 107 deletions(-) diff --git a/tridoracpu/tridoracpu.srcs/irqctrl.v b/tridoracpu/tridoracpu.srcs/irqctrl.v index 1a079bf..b71df60 100644 --- a/tridoracpu/tridoracpu.srcs/irqctrl.v +++ b/tridoracpu/tridoracpu.srcs/irqctrl.v @@ -1,6 +1,6 @@ `timescale 1ns / 1ps -module irqctrl #(IRQ_LINES = 2, IRQ_DELAY_WIDTH = 4) ( +module irqctrl #(IRQ_LINES = 3, IRQ_DELAY_WIDTH = 4) ( input wire clk, input wire [IRQ_LINES-1:0] irq_in, input wire cs, diff --git a/tridoracpu/tridoracpu.srcs/tdraudio.v b/tridoracpu/tridoracpu.srcs/tdraudio.v index cef16da..b9ecc94 100644 --- a/tridoracpu/tridoracpu.srcs/tdraudio.v +++ b/tridoracpu/tridoracpu.srcs/tdraudio.v @@ -1,6 +1,6 @@ `timescale 1ns / 1ps -// waveform generator module (pulse wave or noise) +// waveform generator module (PCM) module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, AMP_WIDTH=16, AMP_BIAS=32768) ( input wire clk, @@ -12,42 +12,78 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, input wire wr_en, output wire [AMP_WIDTH-1:0] amp_val, - output wire running + output wire running, + output wire irq ); localparam TDRAU_REG_CTL = 0; /* control register */ localparam TDRAU_REG_CLK = 1; /* clock divider register */ localparam TDRAU_REG_AMP = 2; /* amplitude (volume) register */ -// localparam LFSR_WIDTH = 18; -// localparam LFSR_TAP_IDX_1 = 17; -// localparam LFSR_TAP_IDX_2 = 10; -// localparam LFSR_INIT = 'h3CBE6; - - localparam LFSR_WIDTH = 23; - localparam LFSR_INIT = 'h1; - reg channel_enable; reg [CLOCK_DIV_WIDTH-1:0] clock_div; reg [CLOCK_DIV_WIDTH-1:0] div_count; reg amp_phase; - reg [AMP_WIDTH-1:0] amp_start; reg [AMP_WIDTH-1:0] amp_out; - reg noise_enable; - reg [LFSR_WIDTH-1:0] lfsr; - wire [AMP_WIDTH-1:0] noise_out; - reg direct_amp_enable; + wire fifo_wr_en; + wire fifo_rd_en, fifo_full, fifo_empty; + wire [DATA_WIDTH-1:0] fifo_rd_data; - //assign rd_data = {{DATA_WIDTH-8-CLOCK_DIV_WIDTH{1'b0}}, div_count, {7{1'b0}}, channel_enable}; - assign rd_data = {8'b0, amp_start, - {6{1'b0}}, amp_phase, channel_enable}; + fifo #(.ADDR_WIDTH(4), .DATA_WIDTH(16)) sample_buf( + clk, reset, + fifo_wr_en, fifo_rd_en, + wr_data, fifo_rd_data, + fifo_full, + fifo_empty + ); + + assign fifo_rd_en = (div_count == 0) && channel_enable && ~fifo_empty; + assign fifo_wr_en = wr_en && (reg_sel == TDRAU_REG_AMP); + + reg irq_buf, irq_done; + assign irq = irq_buf; + + reg [DATA_WIDTH-1:0] rd_data_buf; + assign rd_data = rd_data_buf; + // assign rd_data = {{DATA_WIDTH-8{1'b0}}, {4{1'b0}}, fifo_full, fifo_empty, amp_phase, channel_enable}; assign amp_val = amp_out; assign running = channel_enable; wire ctl_reg_write = wr_en && (reg_sel == TDRAU_REG_CTL); + /* update read data buffer */ + always @(posedge clk) + begin + rd_data_buf <= {{DATA_WIDTH-8{1'b0}}, {4{1'b0}}, fifo_full, fifo_empty, amp_phase, channel_enable}; + end + + /* irq signal to interrupt controller */ + always @(posedge clk) + begin + if(reset) + irq_buf <= 0; + else + if(fifo_empty && ~irq_done) + irq_buf <= 1; + else + irq_buf <= 0; + end + + /* interrupt done flag, used to ensure the irq signal is set for just one clock tick */ + always @(posedge clk) + begin + if(reset) + irq_done <= 0; + else + if(rd_en) // reset irq done flag on any register read + irq_done <= 0; + else + if(irq_buf) + irq_done <= 1; + end + /* channel enable flag */ always @(posedge clk) begin @@ -67,16 +103,6 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, clock_div <= wr_data; end - /* amplitude register */ - always @(posedge clk) - begin - if(reset) - amp_start <= 0; - else - if (wr_en && (reg_sel == TDRAU_REG_AMP)) - amp_start <= wr_data; - end - /* divider counter */ always @(posedge clk) begin @@ -92,43 +118,6 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, div_count <= 1; // start cycle on next clock tick end - - /* noise enable flag */ - always @(posedge clk) - begin - if(reset) - noise_enable <= 0; - else if (ctl_reg_write) - noise_enable <= wr_data[1]; - end - - /* noise generator (Linear Feedback Shift Register) */ - always @(posedge clk) - begin - if (reset) - lfsr <= LFSR_INIT; - else - if (ctl_reg_write && wr_data[1]) - lfsr <= LFSR_INIT; - else - if (channel_enable && noise_enable) - if (div_count == 0) - //lfsr <= { lfsr[LFSR_TAP_IDX_1] ^ lfsr[LFSR_TAP_IDX_2], lfsr[LFSR_WIDTH-1:1] }; - // shift width and tap bits taken from https://github.com/jotego/jtopl - lfsr <= { lfsr[LFSR_WIDTH-2:0], lfsr[22] ^ lfsr[9] ^ lfsr[8] ^ lfsr[0]}; - end - - assign noise_out = lfsr[0] ? amp_start : ~amp_start; - - /* direct amplitude enable flag */ - always @(posedge clk) - begin - if(reset) - direct_amp_enable <= 0; - else if (ctl_reg_write) - direct_amp_enable <= wr_data[2]; - end - /* amplitude out */ always @(posedge clk) begin @@ -142,9 +131,7 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, begin if (div_count == 0) // invert amplitude on clock tick begin - amp_out <= direct_amp_enable ? amp_start : - noise_enable ? noise_out : - amp_phase ? amp_start : ~amp_start; + amp_out <= fifo_rd_data; amp_phase <= ~amp_phase; end end @@ -167,7 +154,7 @@ module tdraudio #(DATA_WIDTH=32) ( input wire [DATA_WIDTH-1:0] wr_data, input wire rd_en, input wire wr_en, - + output wire irq_out, output wire pdm_out, output wire gain_sel, output wire shutdown_n @@ -175,6 +162,7 @@ module tdraudio #(DATA_WIDTH=32) ( localparam CLOCK_DIV_WIDTH = 22; localparam AMP_WIDTH = 16; + localparam AMP_BIAS = 32768; localparam DAC_WIDTH = 18; wire [4:0] chan_sel = io_addr[6:2]; @@ -183,6 +171,7 @@ module tdraudio #(DATA_WIDTH=32) ( wire [AMP_WIDTH-1:0] chan0_amp; wire [DATA_WIDTH-1:0] chan0_rd_data; wire chan0_running; + wire chan0_irq; wire chan0_sel = chan_sel == 5'd0; wire chan0_rd_en = chan0_sel && rd_en; wire chan0_wr_en = chan0_sel && wr_en; @@ -190,6 +179,7 @@ module tdraudio #(DATA_WIDTH=32) ( wire [AMP_WIDTH-1:0] chan1_amp; wire [DATA_WIDTH-1:0] chan1_rd_data; wire chan1_running; + wire chan1_irq; wire chan1_sel = chan_sel == 5'd1; wire chan1_rd_en = chan1_sel && rd_en; wire chan1_wr_en = chan1_sel && wr_en; @@ -197,6 +187,7 @@ module tdraudio #(DATA_WIDTH=32) ( wire [AMP_WIDTH-1:0] chan2_amp; wire [DATA_WIDTH-1:0] chan2_rd_data; wire chan2_running; + wire chan2_irq; wire chan2_sel = chan_sel == 5'd2; wire chan2_rd_en = chan2_sel && rd_en; wire chan2_wr_en = chan2_sel && wr_en; @@ -204,14 +195,13 @@ module tdraudio #(DATA_WIDTH=32) ( wire [AMP_WIDTH-1:0] chan3_amp; wire [DATA_WIDTH-1:0] chan3_rd_data; wire chan3_running; + wire chan3_irq; wire chan3_sel = chan_sel == 5'd3; wire chan3_rd_en = chan3_sel && rd_en; wire chan3_wr_en = chan3_sel && wr_en; wire running = chan0_running || chan1_running || chan2_running || chan3_running; - reg was_running; - assign rd_data = chan0_sel ? chan0_rd_data : chan1_sel ? chan1_rd_data : chan2_sel ? chan2_rd_data : @@ -221,38 +211,38 @@ module tdraudio #(DATA_WIDTH=32) ( wavegen chan0(clk, reset, reg_sel, chan0_rd_data, wr_data, chan0_rd_en, chan0_wr_en, - chan0_amp, chan0_running); + chan0_amp, + chan0_running, chan0_irq); wavegen chan1(clk, reset, reg_sel, chan1_rd_data, wr_data, chan1_rd_en, chan1_wr_en, - chan1_amp, chan1_running); + chan1_amp, + chan1_running, chan1_irq); wavegen chan2(clk, reset, reg_sel, chan2_rd_data, wr_data, chan2_rd_en, chan2_wr_en, - chan2_amp, chan2_running); + chan2_amp, + chan2_irq, chan2_running); wavegen chan3(clk, reset, reg_sel, chan3_rd_data, wr_data, chan3_rd_en, chan3_wr_en, - chan3_amp, chan3_running); + chan3_amp, + chan3_running, chan3_irq); + + reg irq_out_buf; + assign irq_out = irq_out_buf; reg [DAC_WIDTH:0] deltasigma_acc; // one extra bit wire [DAC_WIDTH:0] amp_sum = chan0_amp + chan1_amp + chan2_amp + chan3_amp; // also one overflow bit here - //wire [AMP_WIDTH-1:0] amp_sum_scaled = amp_sum[DAC_WIDTH-2:2]; // shifted right to scale down assign gain_sel = 1; // gain select: 0 -> 12dB, 1 -> 6dB - assign shutdown_n = running; + // assign shutdown_n = running; + assign shutdown_n = 1; /* don't enable shutdown mode, it creates a mains hum */ - /* detect shutdown */ - always @(posedge clk) - begin - if (reset) - was_running <= 0; - else - was_running <= running; - end + always @(posedge clk) irq_out_buf <= chan0_irq || chan1_irq || chan2_irq || chan3_irq; /* delta-sigma DAC */ always @(posedge clk) @@ -260,11 +250,10 @@ module tdraudio #(DATA_WIDTH=32) ( if(reset) deltasigma_acc <= 0; else - if (running) +// if (running) deltasigma_acc <= deltasigma_acc[DAC_WIDTH-1:0] + amp_sum; - else - if (!running && was_running) // clear accumulator on shutdown - deltasigma_acc <= 0; +// else +// deltasigma_acc <= deltasigma_acc[DAC_WIDTH-1:0] + (4*AMP_BIAS); end /* 1-bit audio output */ diff --git a/tridoracpu/tridoracpu.srcs/top.v b/tridoracpu/tridoracpu.srcs/top.v index 4d0135e..6a70ef0 100644 --- a/tridoracpu/tridoracpu.srcs/top.v +++ b/tridoracpu/tridoracpu.srcs/top.v @@ -228,19 +228,12 @@ module top( assign uart_tx_data = mem_write_data[7:0]; assign uart_rd_data = { {WIDTH-10{1'b1}}, uart_rx_avail, uart_tx_busy, uart_rx_data }; - // interrupt controller - reg timer_tick; - reg[23:0] tick_count; - wire [1:0] irq_in = { timer_tick, uart_rx_avail }; - wire [1:0] irqc_rd_data0; - wire [WIDTH-1:0] irqc_rd_data = { tick_count, 6'b0, irqc_rd_data0 }; - wire irqc_seten = mem_write_data[7]; - wire irqc_cs = io_enable && (io_slot == 3); - + wire audio_irq; `ifdef ENABLE_TDRAUDIO wire [WIDTH-1:0] tdraudio_wr_data; wire [WIDTH-1:0] tdraudio_rd_data; wire tdraudio_rd_en, tdraudio_wr_en; + wire tdraudio_irq; wire tdraudio_cs_en = io_enable && (io_slot == 4); assign tdraudio_rd_en = tdraudio_cs_en && mem_read_enable; @@ -253,9 +246,20 @@ module top( tdraudio_wr_data, tdraudio_rd_en, tdraudio_wr_en, + tdraudio_irq, amp2_ain, amp2_gain, amp2_shutdown_n); + assign audio_irq = tdraudio_irq; `endif + // interrupt controller + reg timer_tick; + reg[23:0] tick_count; + wire [2:0] irq_in = { audio_irq, timer_tick, uart_rx_avail }; + wire [2:0] irqc_rd_data0; + wire [WIDTH-1:0] irqc_rd_data = { tick_count, 5'b0, irqc_rd_data0 }; + wire irqc_seten = mem_write_data[7]; + wire irqc_cs = io_enable && (io_slot == 3); + assign io_rd_data = (io_slot == 0) ? uart_rd_data : `ifdef ENABLE_MICROSD (io_slot == 1) ? spi_rd_data : diff --git a/tridoracpu/tridoracpu.xpr b/tridoracpu/tridoracpu.xpr index 15b43f2..3b60ca9 100644 --- a/tridoracpu/tridoracpu.xpr +++ b/tridoracpu/tridoracpu.xpr @@ -378,25 +378,23 @@ - + - - Uses multiple algorithms for optimization, placement, and routing to get potentially better results. + + Includes alternate algorithms for timing-driven optimization - - - + - + - + From 5c00dfcec971fe48d16abb7bc9e9c365e42f9de7 Mon Sep 17 00:00:00 2001 From: slederer Date: Tue, 7 Oct 2025 00:37:53 +0200 Subject: [PATCH 02/11] tdraudio: add irq_enable flag, add pcmaudio library runtime: disable interrupts on PTERM stdlib: check for error state in FileSize --- examples/pcmtest.pas | 47 +++++ examples/pcmtest2.pas | 60 +++++++ lib/pcmaudio.inc | 7 + lib/pcmaudio.s | 241 ++++++++++++++++++++++++++ lib/runtime.s | 6 + lib/stdlib.pas | 2 + tridoracpu/tridoracpu.srcs/tdraudio.v | 31 ++-- tridoracpu/tridoracpu.xpr | 17 +- 8 files changed, 390 insertions(+), 21 deletions(-) create mode 100644 examples/pcmtest.pas create mode 100644 examples/pcmtest2.pas create mode 100644 lib/pcmaudio.inc create mode 100644 lib/pcmaudio.s diff --git a/examples/pcmtest.pas b/examples/pcmtest.pas new file mode 100644 index 0000000..423faaf --- /dev/null +++ b/examples/pcmtest.pas @@ -0,0 +1,47 @@ +{$H1536} +program pcmtest; +uses pcmaudio; + +var filename:string; + buf:SndBufPtr; + f:file; + size:integer; + i:integer; + c:char; + sampleRate:integer; + err:integer; +begin + if ParamCount > 0 then + filename := ParamStr(1) + else + begin + write('Filename> '); + readln(filename); + end; + + err := 1; + if ParamCount > 1 then + val(ParamStr(2),sampleRate, err); + + if err <> 0 then + sampleRate := 16000; + + open(f, filename, ModeReadOnly); + size := FileSize(f); + new(buf, size); + + buf^ := ''; + write('Reading ', size, ' bytes...'); + for i := 1 to size do + begin + read(f,c); + AppendChar(buf^,c); + end; + writeln; + + close(f); + + PlaySample(buf, sampleRate); + + dispose(buf); +end. diff --git a/examples/pcmtest2.pas b/examples/pcmtest2.pas new file mode 100644 index 0000000..56ecb95 --- /dev/null +++ b/examples/pcmtest2.pas @@ -0,0 +1,60 @@ +{$H1536} +program pcmtest2; +uses pcmaudio; + +var filename:string; + buf:SndBufPtr; + f:file; + size:integer; + i:integer; + c:char; + sampleRate:integer; + err:integer; + done:boolean; +begin + if ParamCount > 0 then + filename := ParamStr(1) + else + begin + write('Filename> '); + readln(filename); + end; + + err := 1; + if ParamCount > 1 then + val(ParamStr(2), sampleRate, err); + if err > 0 then + sampleRate := 16000; + + open(f, filename, ModeReadOnly); + size := FileSize(f); + new(buf, size); + + buf^ := ''; + write('Reading ', size, ' bytes...'); + for i := 1 to size do + begin + read(f,c); + AppendChar(buf^,c); + end; + writeln; + + close(f); + + SampleQStart(buf, sampleRate); + + write('Press Q to stop> '); + done := false; + while not done do + begin + read(c); + if upcase(c) = 'Q' then + done := true + else + writeln('Queue size: ', SampleQSize); + end; + + SampleQStop; + + dispose(buf); +end. diff --git a/lib/pcmaudio.inc b/lib/pcmaudio.inc new file mode 100644 index 0000000..4c3cdb3 --- /dev/null +++ b/lib/pcmaudio.inc @@ -0,0 +1,7 @@ +type SndBuf = string[32768]; +type SndBufPtr = ^SndBuf; + +procedure PlaySample(buf:SndBufPtr;sampleRate:integer); external; +procedure SampleQStart(buf:SndBufPtr;sampleRate:integer); external; +procedure SampleQStop; external; +function SampleQSize:integer; external; diff --git a/lib/pcmaudio.s b/lib/pcmaudio.s new file mode 100644 index 0000000..a030985 --- /dev/null +++ b/lib/pcmaudio.s @@ -0,0 +1,241 @@ + .EQU AUDIO_BASE $A00 + .EQU IRQC_REG $980 + .EQU IRQC_EN $80 + +; args: sample rate +START_PCMAUDIO: + ; calculate clock divider + LOADCP 77000000 + SWAP + LOADCP _DIV + CALL + + LOADC AUDIO_BASE + 1 + SWAP ; put clock divider on ToS +; LOADCP 4812 ; clock divider for 16KHz sample rate +; LOADCP 2406 ; clock divider for 32KHz sample rate + STOREI 1 + LOADCP 32768 ; set amplitude to biased 0 + STOREI + DROP + LOADC AUDIO_BASE + LOADC 17 ; enable channel, enable interrupt + STOREI + DROP + RET + +STOP_AUDIO: + LOADC AUDIO_BASE + LOADC 0 + STOREI + DROP + RET + +; args: pointer to pascal string, sample rate + .EQU PS_PTR 0 + .EQU PS_COUNT 4 + .EQU PS_FS 12 +PLAYSAMPLE: + FPADJ -PS_FS + + LOADCP START_PCMAUDIO + CALL + + DUP + LOADI ; get string size from header + SHR ; divide by 4 to get word count + SHR + + STORE PS_COUNT + INC 8 ; skip rest of header + STORE PS_PTR ; store sample data pointer + +PS_L0: + LOAD PS_PTR ; load pointer + INC.S1.X2Y 4 ; increment and keep old value + STORE PS_PTR ; store incremented value + + LOADI ; load 32 bit word + DUP + BROT ; get upper 16 bit word + BROT + LOADCP $FFFF + AND + + LOADCP PLAY_1SAMPLE + CALL + + LOADCP $FFFF ; get lower 16 bit word + AND + LOADCP PLAY_1SAMPLE + CALL + + LOAD PS_COUNT ; load word count + DEC 1 ; decrement + DUP + STORE PS_COUNT + CBRANCH.NZ PS_L0 ; loop if not zero + + LOADCP STOP_AUDIO + CALL + + FPADJ PS_FS + RET + +; play one sample, waiting +; for the clock divider, which +; is visible via the phase flag +; args: 16-bit unsigned sample +PLAY_1SAMPLE: + +PLAY1_L0: + LOADC AUDIO_BASE + LOADI + LOADC 8 ; get fifo_full flag + AND + CBRANCH.NZ PLAY1_L0 ; loop if fifo is full + + LOADC AUDIO_BASE+2 ; store amplitude value + SWAP + STOREI + DROP + RET + +; start interrupt-driven sample playback +; args: pointer to pascal string, sample rate +SAMPLEQSTART: + LOADCP START_PCMAUDIO + CALL + + LOADCP SMPLQ_COUNT + OVER + LOADI ; get string size from header + SHR ; divide by 4 to get word count + SHR + + STOREI + DROP + + LOADCP SMPLQ_PTR + SWAP + INC 8 ; skip rest of header + STOREI ; store sample data pointer + DROP + + LOADCP SMPLQ_ISR ; set interrupt handler + STOREREG IV + + LOADC IRQC_REG ; enable irq + LOADC IRQC_EN + STOREI + DROP + + RET + +SAMPLEQSTOP: + LOADCP SMPLQ_PTR + LOADC 0 + STOREI + DROP + + LOADCP STOP_AUDIO + CALL + + LOADC IRQC_REG ; disable irq + LOADC 0 + STOREI + DROP + RET + +SAMPLEQSIZE: + LOADCP SMPLQ_COUNT + LOADI + RET + +SMPLQ_PTR: .WORD 0 +SMPLQ_COUNT: .WORD 0 + +SMPLQ_ISR: + LOADC IRQC_REG + LOADI + LOADC 4 ; check for audio interrupt + AND + CBRANCH.Z SMPLQ_I_XT ; if flag not set, exit + +SMPLQ_I_L: + LOADCP SMPLQ_PTR + LOADI ; load word pointer + DUP + CBRANCH.NZ SMPLQ_I_B ; check for null pointer + DROP + BRANCH SMPLQ_I_XT ; if null, end interrupt routine +SMPLQ_I_B: + LOADI ; load next word + DUP + + BROT ; get high half-word + BROT + LOADCP $FFFF + AND + + LOADC AUDIO_BASE+2 + SWAP + STOREI ; write sample, keep addr + + SWAP ; addr to NoS, lower halfword on ToS + LOADCP $FFFF + AND + STOREI ; write sample + DROP + + ; decrement word count + LOADCP SMPLQ_COUNT + LOADI.S1.X2Y ; load counter, keep addr + DEC 1 + DUP + CBRANCH.Z SMPLQ_I_END ; end if zero + + STOREI ; store new counter value + DROP + + ; increment pointer + LOADCP SMPLQ_PTR + LOADI.S1.X2Y + INC 4 + STOREI + DROP + + ; check if fifo is full + LOADC AUDIO_BASE + LOADI + LOADC 8 ; fifo_full + AND + CBRANCH.Z SMPLQ_I_L ; next sample if not full + + LOADC AUDIO_BASE + LOADC 17 ; re-enable channel interrupt + STOREI + DROP + + BRANCH SMPLQ_I_XT + + ; end playback, set ptr and counter to zero +SMPLQ_I_END: + DROP + DROP + LOADCP SMPLQ_PTR + LOADC 0 + STOREI + DROP + LOADCP SMPLQ_COUNT + LOADC 0 + STOREI + DROP + +SMPLQ_I_XT: + LOADC IRQC_REG ; re-enable interrupts + LOADC IRQC_EN + STOREI + DROP + LOADREG IR ; jump via interrupt return register + JUMP diff --git a/lib/runtime.s b/lib/runtime.s index ce60b8d..9eb35d7 100644 --- a/lib/runtime.s +++ b/lib/runtime.s @@ -1931,6 +1931,12 @@ _CLEARESTACK_XT: ; Terminate program: clear estack and ; jump to coreloader PTERM: + ; just to be safe, disable interrupts + LOADC $980 + LOADC 0 + STOREI + DROP + LOADCP _CLEARESTACK CALL LOADCP LOADER_START diff --git a/lib/stdlib.pas b/lib/stdlib.pas index dd6294c..84025b3 100644 --- a/lib/stdlib.pas +++ b/lib/stdlib.pas @@ -1844,6 +1844,8 @@ end; function filesize(var fil:file):integer; begin + checkerror(fil); + if fil.typ = IOChannel then filesize := -1 else diff --git a/tridoracpu/tridoracpu.srcs/tdraudio.v b/tridoracpu/tridoracpu.srcs/tdraudio.v index b9ecc94..0cc055e 100644 --- a/tridoracpu/tridoracpu.srcs/tdraudio.v +++ b/tridoracpu/tridoracpu.srcs/tdraudio.v @@ -42,12 +42,12 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, assign fifo_rd_en = (div_count == 0) && channel_enable && ~fifo_empty; assign fifo_wr_en = wr_en && (reg_sel == TDRAU_REG_AMP); - reg irq_buf, irq_done; - assign irq = irq_buf; + reg irq_buf, irq_enable; + assign irq = channel_enable && irq_buf; reg [DATA_WIDTH-1:0] rd_data_buf; assign rd_data = rd_data_buf; - // assign rd_data = {{DATA_WIDTH-8{1'b0}}, {4{1'b0}}, fifo_full, fifo_empty, amp_phase, channel_enable}; + assign amp_val = amp_out; assign running = channel_enable; @@ -56,7 +56,8 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, /* update read data buffer */ always @(posedge clk) begin - rd_data_buf <= {{DATA_WIDTH-8{1'b0}}, {4{1'b0}}, fifo_full, fifo_empty, amp_phase, channel_enable}; + rd_data_buf <= {{DATA_WIDTH-8{1'b0}}, + {3{1'b0}}, irq_enable, fifo_full, fifo_empty, amp_phase, channel_enable}; end /* irq signal to interrupt controller */ @@ -65,23 +66,23 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, if(reset) irq_buf <= 0; else - if(fifo_empty && ~irq_done) + if(fifo_empty && irq_enable) irq_buf <= 1; else irq_buf <= 0; end - /* interrupt done flag, used to ensure the irq signal is set for just one clock tick */ + /* interrupt enable flag */ always @(posedge clk) begin if(reset) - irq_done <= 0; + irq_enable <= 0; else - if(rd_en) // reset irq done flag on any register read - irq_done <= 0; + if(ctl_reg_write) + irq_enable <= wr_data[4]; else if(irq_buf) - irq_done <= 1; + irq_enable <= 0; // disable interrupts after an interrupt end /* channel enable flag */ @@ -139,8 +140,8 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, amp_out <= AMP_BIAS; // reset phase bit when enabling the channel - if (ctl_reg_write && wr_data[0]) - // when channel is enabled, phase will be flipped on next tick + if (ctl_reg_write && wr_data[0] && ~channel_enable) + // when channel is being enabled, phase will be flipped on next tick // because div_count will become zero amp_phase <= 1; end @@ -232,9 +233,6 @@ module tdraudio #(DATA_WIDTH=32) ( chan3_amp, chan3_running, chan3_irq); - reg irq_out_buf; - assign irq_out = irq_out_buf; - reg [DAC_WIDTH:0] deltasigma_acc; // one extra bit wire [DAC_WIDTH:0] amp_sum = chan0_amp + chan1_amp + chan2_amp + chan3_amp; // also one overflow bit here assign gain_sel = 1; // gain select: 0 -> 12dB, 1 -> 6dB @@ -242,6 +240,9 @@ module tdraudio #(DATA_WIDTH=32) ( // assign shutdown_n = running; assign shutdown_n = 1; /* don't enable shutdown mode, it creates a mains hum */ + reg irq_out_buf; + assign irq_out = irq_out_buf; + always @(posedge clk) irq_out_buf <= chan0_irq || chan1_irq || chan2_irq || chan3_irq; /* delta-sigma DAC */ diff --git a/tridoracpu/tridoracpu.xpr b/tridoracpu/tridoracpu.xpr index 3b60ca9..3767063 100644 --- a/tridoracpu/tridoracpu.xpr +++ b/tridoracpu/tridoracpu.xpr @@ -378,16 +378,18 @@ - + - - Includes alternate algorithms for timing-driven optimization + + Similar to Performance_ExplorePostRoutePhysOpt, but enables logic optimization step (opt_design) with the ExploreWithRemap directive. - + + + - + @@ -395,8 +397,11 @@ + + + + - From 598ee8921ff7c22ca74cd4399ed3e2f799bd06ce Mon Sep 17 00:00:00 2001 From: slederer Date: Tue, 7 Oct 2025 01:16:25 +0200 Subject: [PATCH 03/11] tdraudio: add documentation --- doc/irqctrl.md | 3 +- doc/tdraudio.md | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 doc/tdraudio.md diff --git a/doc/irqctrl.md b/doc/irqctrl.md index 580c123..bf76999 100644 --- a/doc/irqctrl.md +++ b/doc/irqctrl.md @@ -9,12 +9,13 @@ The interrupt controller uses a single register at address: $980 |_bit_ |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00| |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- | -|_Value_|t |t |t |t |t |t |t |t |- |- |- |- |- |- |p1 |p0 | +|_Value_|t |t |t |t |t |t |t |t |- |- |- |- |- |p2 |p1 |p0 | |Bitfields|Description| |---------|-----------| | _t_ | unsigned 24 bit counter of timer ticks since reset +| _p2_ | IRQ 2 (audio) interrupt pending if 1 | _p1_ | IRQ 1 (timer tick) interrupt pending if 1 | _p0_ | IRQ 0 (UART) interrupt pending if 1 diff --git a/doc/tdraudio.md b/doc/tdraudio.md new file mode 100644 index 0000000..999ebfc --- /dev/null +++ b/doc/tdraudio.md @@ -0,0 +1,104 @@ +# Audio Controller +The audio controller provides four channels of 16-bit PCM audio playback. + +It uses multiple registers starting at address $A00. + +Each of the four channels has three registers. + +For the first channel the register addresses are: + +|Address|Description| +|-------|-----------| +| $A00 | Control Register | +| $A01 | Clock Divider Register | +| $A02 | Amplitude Register | + +The register addresses for the second channel start at $A04, +the third channel at $A08 +and the fourth channel at $A0C. + +## Reading the control register + +|_bit_ |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16| +|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- | +|_Value_|- |- |- |- |- |- |- |- |- |-|- |- |- |- |- |- | + +|_bit_ |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00| +|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- | +|_Value_|- |- |- |- |- |- |- |- |- |- |- |i |f | e | p | c | + + +|Bitfields|Description| +|---------|-----------| +| _i_ | interrupt is enabled for this channel when 1 | +| _f_ | sample buffer is full when 1 | +| _e_ | sample buffer is empty when 1 | +| _p_ | changes from 0 to 1 and vice versa on each sample clock | +| _c_ | channel is enabled if 1 | + +## Writing the control register + +|_bit_ |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16| +|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- | +|_Value_|- |- |- |- |- |- |- |- |- |-|- |- |- |- |- |- | + +|_bit_ |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00| +|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- | +|_Value_|- |- |- |- |- |- |- |- |- |- |- |i |- | - | - | c | + + +|Bitfields|Description| +|---------|-----------| +| _c_ | enable channel if 1, disable if 0 | +| _i_ | enable channel interrupt if 1, disable if 0 | + + +## Writing the clock divider register + +|_bit_ |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16| +|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- | +|_Value_|d |d |d |d |d |d |d |d |d |d|d |d |d |d |d |d | + +|_bit_ |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00| +|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- | +|_Value_|d |d |d |d |d |d |d |d |d |d|d |d |d |d |d |d | + + +|Bitfields|Description| +|---------|-----------| +| _d_ | an unsigned 32-bit value for the clock divider | + + +## Writing the amplitude register + +|_bit_ |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16| +|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- | +|_Value_|- |- |- |- |- |- |- |- |- |-|- |- |- |- |- |- | + +|_bit_ |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00| +|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- | +|_Value_|a |a |a |a |a |a |a |a |a |a |a |a |a | a | a | a | + + +|Bitfields|Description| +|---------|-----------| +| _a_ | an unsigned 16-bit value for the amplitude (sample) value with a bias of 32768 | + +## Notes +The clock divider specifies the number of CPU clock ticks between two samples. + +Writing to the amplitude register adds the sample value to the sample buffer. The sample buffer is organized as a FIFO with 16 elements. + +Amplitude (sample) values are represented as unsigned, biased 16-bit numbers. The bias is 32768, so given an amplitude range of 1.0 to -1.0, a 1.0 is represented by 65535, 0.0 by 32768 and -1.0 by 0. + +Interrupt processing needs to be enabled for each channel if required. + +An interrupt on any channel will be signalled to the interrupt controller +as IRQ 2. The interrupt service routine should check all running channels +for an emtpy buffer. + +If an audio interrupt has occured on a channel, the interrupt enable flag +is cleared for that channel. It needs to be re-enabled in the interrupt service routine. + +Interrupts also need to be enabled on the interrupt controller, +and re-enabled there after each interrupt. From 536c0adde72c7e81aecd4b45398980459287c8b9 Mon Sep 17 00:00:00 2001 From: slederer Date: Sun, 12 Oct 2025 22:52:17 +0200 Subject: [PATCH 04/11] pcmaudio: set amplitude to biased zero at end pcmtest2: small updates to the demo program --- examples/pcmtest2.pas | 62 ++++++++++++++++++++++++++----------------- lib/pcmaudio.s | 6 +++++ 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/examples/pcmtest2.pas b/examples/pcmtest2.pas index 56ecb95..f72e5e6 100644 --- a/examples/pcmtest2.pas +++ b/examples/pcmtest2.pas @@ -1,32 +1,21 @@ -{$H1536} +{$H2560} program pcmtest2; uses pcmaudio; var filename:string; buf:SndBufPtr; - f:file; - size:integer; - i:integer; - c:char; sampleRate:integer; err:integer; done:boolean; + c:char; + +function readAudioFile(fname:string):SndBufPtr; +var i,size:integer; + c:char; + buf:SndBufPtr; + f:file; begin - if ParamCount > 0 then - filename := ParamStr(1) - else - begin - write('Filename> '); - readln(filename); - end; - - err := 1; - if ParamCount > 1 then - val(ParamStr(2), sampleRate, err); - if err > 0 then - sampleRate := 16000; - - open(f, filename, ModeReadOnly); + open(f, fname, ModeReadOnly); size := FileSize(f); new(buf, size); @@ -41,17 +30,42 @@ begin close(f); + readAudioFile := buf; +end; + +begin + if ParamCount > 0 then + filename := ParamStr(1) + else + begin + write('Filename> '); + readln(filename); + end; + + err := 1; + if ParamCount > 1 then + val(ParamStr(2), sampleRate, err); + if err > 0 then + sampleRate := 32000; + + buf := readAudioFile(filename); + SampleQStart(buf, sampleRate); - write('Press Q to stop> '); + write('Press ESC to stop> '); done := false; while not done do begin read(c); - if upcase(c) = 'Q' then - done := true + if c = #27 then + begin + done := true; writeln(';'); + end else - writeln('Queue size: ', SampleQSize); + if c = '?' then + begin + writeln; writeln('Queue: ', SampleQSize); + end; end; SampleQStop; diff --git a/lib/pcmaudio.s b/lib/pcmaudio.s index a030985..d1add4f 100644 --- a/lib/pcmaudio.s +++ b/lib/pcmaudio.s @@ -232,6 +232,12 @@ SMPLQ_I_END: STOREI DROP + ; set amplitude out to zero (biased) + LOADC AUDIO_BASE+2 + LOADCP 32768 + STOREI + DROP + SMPLQ_I_XT: LOADC IRQC_REG ; re-enable interrupts LOADC IRQC_EN From 3c7cf636a4b11b030dd0b76dd2cd218afbccfff5 Mon Sep 17 00:00:00 2001 From: slederer Date: Mon, 13 Oct 2025 00:47:41 +0200 Subject: [PATCH 05/11] utils: add audio conversion script, update image creation --- utils/tdrimg.py | 5 +++++ utils/wav2tdrau.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 utils/wav2tdrau.py diff --git a/utils/tdrimg.py b/utils/tdrimg.py index be93ed9..b7ce4cb 100644 --- a/utils/tdrimg.py +++ b/utils/tdrimg.py @@ -538,6 +538,9 @@ def create_image_with_stuff(imgfile): slotnr = putfile("../progs/recover.prog", None , f, part, partstart, slotnr) slotnr = putfile("../progs/changemem.prog", None , f, part, partstart, slotnr) + slotnr = putfile("../lib/pcmaudio.s", None , f, part, partstart, slotnr) + slotnr = putfile("../lib/pcmaudio.inc", None , f, part, partstart, slotnr) + listdir(f, part) # third partition @@ -600,6 +603,8 @@ def create_image_with_stuff(imgfile): slotnr = putfile("../examples/lines.pas", None , f, part, partstart, slotnr) + slotnr = putfile("../examples/pcmtest2.pas", None , f, part, partstart, slotnr) + slotnr = putfile("../examples/pictviewer.pas", None , f, part, partstart, slotnr) slotnr = putfile("../examples/Toco_Toucan.pict", None , f, part, partstart, slotnr) slotnr = putfile("../examples/shinkansen.pict", None , f, part, partstart, slotnr) diff --git a/utils/wav2tdrau.py b/utils/wav2tdrau.py new file mode 100644 index 0000000..16230d6 --- /dev/null +++ b/utils/wav2tdrau.py @@ -0,0 +1,29 @@ +import sys +import random, struct +import wave + +freq = 16000 +BIAS = 32768 +def convert(srcpath, destpath): + outdata = bytearray() + with wave.open(srcpath, mode="rb") as f: + params = f.getparams() + print(params.nchannels, params.sampwidth, params.framerate) + + frames = f.readframes(2*1024*1024) + for i in range(0, len(frames), 2): + v = int.from_bytes(frames[i:i+2], "little", signed=True) + v += BIAS + hi = (v & 0xFF00) >> 8 + lo = (v & 0x00FF) + outdata.append(hi) + outdata.append(lo) + + with open(destpath, mode="wb") as f: + f.write(outdata) + + +if __name__ == "__main__": + sourcefilename = sys.argv[1] + destfilename = sys.argv[2] + convert(sourcefilename, destfilename) From d7a025fd08ed0a1a3179a4c839e86407275ddeb2 Mon Sep 17 00:00:00 2001 From: slederer Date: Mon, 13 Oct 2025 23:33:30 +0200 Subject: [PATCH 06/11] update documentation for October 2025 update --- README.md | 21 ++++++++++++++++++++- doc/mem.md | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index be77d37..758c36b 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,25 @@ Other inspirations were, among others, in no particular order: - the Magic-1 by Bill Buzbee - the OPC by revaldinho +## October 2025 Update +This update introduces a data cache for the Tridora-CPU. It is similar to the instruction cache +as it caches the 16 bytes coming from the DRAM memory controller. It is a write-back cache, i.e. +when a word inside the cached area is written, it updates the cache instead of invalidating it. + +This is important because there are many idioms in the stack machine assembly language where you +store a local variable and then read it again (e.g. updating a loop variable). + +Since for most programs, the user stack and parts of the heap are inside the DRAM area, the data cache +has a more noticable impact. In the benchmark program that was already used for the last update, +the data cache results in a 50% improvement for the empty loop test. This is in comparison to the version +without data cache but with the instruction cache, both running code out of DRAM. + +It is also noticable for compile times: With the data cache, compiling and assembling the +"hello,world" program takes 16 seconds instead of 20. With a little tweak of the SD-Card controller +that slightly increased the data transfer rate, the build time goes down to 15 seconds. + +Also, an audio controller was added that allows interrupt-driven sample playback via an AMP2 PMOD. + ## April 2025 Update The clock has been reduced to 77 MHz from 83 MHz. Apparently the design was at the limit and timing problems were cropping up seemingly at random. Reducing the clock speed made some @@ -62,7 +81,7 @@ on the emulator image. - the [Hackaday project](https://hackaday.io/project/198324-tridora-cpu) (mostly copy-paste from this README) - the [YouTube channel](https://www.youtube.com/@tridoracpu/videos) with some demo videos - the [emulator](https://git.insignificance.de/slederer/-/packages/generic/tridoraemu/0.0.5/files/12) (source and windows binary) -- the [FPGA bitstream](https://git.insignificance.de/slederer/-/packages/generic/tdr-bitstream/0.0.2/files/14) for the Arty-A7-35T board +- the [FPGA bitstream](https://git.insignificance.de/slederer/-/packages/generic/tdr-bitstream/0.0.3/files/15) for the Arty-A7-35T board - an [SD-card image](https://git.insignificance.de/slederer/-/packages/generic/tdr-cardimage/0.0.4/files/13) Contact the author here: tridoracpu [at] insignificance.de diff --git a/doc/mem.md b/doc/mem.md index e24fbe2..f7dbc2b 100644 --- a/doc/mem.md +++ b/doc/mem.md @@ -34,3 +34,4 @@ Currently, only I/O slots 0-3 are being used. | 1 | $880 | SPI-SD | | 2 | $900 | VGA | | 3 | $980 | IRQC | +| 4 | $A00 | TDRAUDIO | From 0f72080c56072136be3301530256a2c0ba5bfc3e Mon Sep 17 00:00:00 2001 From: slederer Date: Sun, 26 Oct 2025 00:27:34 +0200 Subject: [PATCH 07/11] tridoracpu: experimented with synthesis options again - workaround for an apparent bug with LOAD address generation at offsets >= 3584 - updated bitstream URL --- README.md | 2 +- tridoracpu/tridoracpu.xpr | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 758c36b..fe930a7 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ on the emulator image. - the [Hackaday project](https://hackaday.io/project/198324-tridora-cpu) (mostly copy-paste from this README) - the [YouTube channel](https://www.youtube.com/@tridoracpu/videos) with some demo videos - the [emulator](https://git.insignificance.de/slederer/-/packages/generic/tridoraemu/0.0.5/files/12) (source and windows binary) -- the [FPGA bitstream](https://git.insignificance.de/slederer/-/packages/generic/tdr-bitstream/0.0.3/files/15) for the Arty-A7-35T board +- the [FPGA bitstream](https://git.insignificance.de/slederer/-/packages/generic/tdr-bitstream/0.0.4/files/16) for the Arty-A7-35T board - an [SD-card image](https://git.insignificance.de/slederer/-/packages/generic/tdr-cardimage/0.0.4/files/13) Contact the author here: tridoracpu [at] insignificance.de diff --git a/tridoracpu/tridoracpu.xpr b/tridoracpu/tridoracpu.xpr index 3767063..30d168a 100644 --- a/tridoracpu/tridoracpu.xpr +++ b/tridoracpu/tridoracpu.xpr @@ -356,12 +356,15 @@ - + - - Vivado Synthesis Defaults + + Performs general area optimizations including changing the threshold for control set optimizations, forcing ternary adder implementation, applying lower thresholds for use of carry chain in comparators and also area optimized mux optimizations. - + + + + @@ -378,14 +381,14 @@ - + - - Similar to Performance_ExplorePostRoutePhysOpt, but enables logic optimization step (opt_design) with the ExploreWithRemap directive. + + Uses multiple algorithms for optimization, placement, and routing to get potentially better results. - + @@ -396,12 +399,9 @@ - - - - + From 87ec71bd6de7a6a649c10be06d16d09b3e264414 Mon Sep 17 00:00:00 2001 From: slederer Date: Wed, 5 Nov 2025 00:30:49 +0100 Subject: [PATCH 08/11] align _END label, add ALIGN directive to assembler - fixes failing memory allocator when _END label is not aligned --- pcomp/emit.pas | 4 +++- pcomp/sasm.pas | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pcomp/emit.pas b/pcomp/emit.pas index a201714..d440951 100644 --- a/pcomp/emit.pas +++ b/pcomp/emit.pas @@ -324,7 +324,9 @@ begin rewindStringList(usedUnits); while nextStringListItem(usedUnits, unitName) do emitInclude(unitName + UnitSuffix2); - + (* _END label needs to be word-aligned because + it is used as the start of the heap *) + emitIns('.ALIGN'); emitLabelRaw('_END'); end; diff --git a/pcomp/sasm.pas b/pcomp/sasm.pas index 1858f11..2af55c3 100644 --- a/pcomp/sasm.pas +++ b/pcomp/sasm.pas @@ -2056,6 +2056,9 @@ begin operandValue := 0; emitBlock(count, operandValue); end + else + if lastToken.tokenText6 = '.ALIGN' then + alignOutput(wordSize) else errorExit2('Unrecognized directive', lastToken.tokenText); end; From 8f4d0176683bef38852d5a2893e651a59666b0eb Mon Sep 17 00:00:00 2001 From: slederer Date: Sun, 30 Nov 2025 23:49:44 +0100 Subject: [PATCH 09/11] sasm: fix typo error; examples: add fire demo --- examples/fastfire.inc | 5 + examples/fastfire.s | 326 ++++++++++++++++++++++++++++++++++++++++++ examples/fire.pas | 76 ++++++++++ examples/fire2.pas | 84 +++++++++++ pcomp/sasm.pas | 2 +- 5 files changed, 492 insertions(+), 1 deletion(-) create mode 100644 examples/fastfire.inc create mode 100644 examples/fastfire.s create mode 100644 examples/fire.pas create mode 100644 examples/fire2.pas diff --git a/examples/fastfire.inc b/examples/fastfire.inc new file mode 100644 index 0000000..bf0dce6 --- /dev/null +++ b/examples/fastfire.inc @@ -0,0 +1,5 @@ +const FIREWIDTH = 319; FIREHEIGHT = 79; (* keep in sync with fastfire.s! *) +type FireBuf = array [0..FIREHEIGHT, 0..FIREWIDTH] of integer; + +procedure FastFireUpdate(var f:FireBuf); external; +procedure FastFireDraw(var f:FireBuf;screenx, screeny:integer); external; diff --git a/examples/fastfire.s b/examples/fastfire.s new file mode 100644 index 0000000..f0e10e4 --- /dev/null +++ b/examples/fastfire.s @@ -0,0 +1,326 @@ + ; width and height of the fire cell matrix + ; Be sure to sync this with fastfire.inc! + .EQU FIREWIDTH 319 + .EQU FIREHEIGHT 79 + + ; + ; The cell matrix actually has one column + ; and one row more than FIREWIDTH and + ; FIREHEIGHT to handle the negative + ; X offsets when calculating new + ; cell values. + ; Likewise, there is one more row. + ; So rows are processed from 0 to FIREHEIGHT - 2 + ; and columms from 1 to FIREWIDTH - 1. + + ; cells considered for calculating new + ; value for cell O (reference cells): + ; .....O...... + ; ....123..... + ; .....4...... + +; args: pointer to fire cell buffer + .EQU FF_ROW_COUNT 0 + .EQU FF_COL_COUNT 4 + .EQU FF_ROW_OFFS 8 + .EQU FF_OFFS1 12 + .EQU FF_OFFS2 16 + .EQU FF_OFFS3 20 + .EQU FF_OFFS4 24 + .EQU FF_CELL_PTR 28 + .EQU FF_FS 32 +FASTFIREUPDATE: + FPADJ -FF_FS + STORE FF_CELL_PTR + LOADC FIREHEIGHT-1 + STORE FF_ROW_COUNT + + ; calculate offsets for reference cells + LOADC FIREWIDTH+1 + SHL 2 + DUP + STORE FF_ROW_OFFS ; offset to next row: WIDTH*4 + DEC 4 ; offset to cell 1: row offset - 4 + DUP + STORE FF_OFFS1 + INC 4 + DUP + STORE FF_OFFS2 ; offset to cell 2: + 4 + INC 4 + STORE FF_OFFS3 ; offset to cell 3: + 4 + LOAD FF_ROW_OFFS + SHL 1 ; offset to cell 4: row offset * 2 + STORE FF_OFFS4 + + ; start at column 1 + LOAD FF_CELL_PTR + INC 4 + STORE FF_CELL_PTR +FF_ROW: + LOADC FIREWIDTH-1 + STORE FF_COL_COUNT + +FF_COL: + LOAD FF_CELL_PTR + LOAD FF_OFFS1 + ADD + LOADI + + LOAD FF_CELL_PTR + LOAD FF_OFFS2 + ADD + LOADI + + LOAD FF_CELL_PTR + LOAD FF_OFFS3 + ADD + LOADI + + LOAD FF_CELL_PTR + LOAD FF_OFFS4 + ADD + LOADI + + ADD + ADD + ADD + + SHR + SHR + + ; if new cell value > 0, subtract 1 to cool down + DUP + CBRANCH.Z FF_SKIP + DEC 1 +FF_SKIP: + LOAD FF_CELL_PTR ; load cell ptr + SWAP ; swap with new value + STOREI 4 ; store with postincrement + STORE FF_CELL_PTR ; save new ptr value + + LOAD FF_COL_COUNT ; decrement column count + DEC 1 + DUP + STORE FF_COL_COUNT + CBRANCH.NZ FF_COL ; loop if col count <> 0 + + ; at the end of a row, go to next row + ; by adding 8 to the cell pointer, + ; skipping the first cell of the next row + LOAD FF_CELL_PTR + INC 8 + STORE FF_CELL_PTR + + LOAD FF_ROW_COUNT ; decrement row count + DEC 1 + DUP + STORE FF_ROW_COUNT + CBRANCH.NZ FF_ROW ; loop if row count <> 0 + +FF_EXIT: + FPADJ FF_FS + RET + +; framebuffer controller registers + .EQU FB_RA $900 + .EQU FB_WA $901 + .EQU FB_IO $902 + .EQU FB_PS $903 + .EQU FB_PD $904 + .EQU FB_CTL $905 + .EQU WORDS_PER_LINE 80 + +; fire width in vmem words (strict left-to-right evaluation) + .EQU FFD_ROW_WORDS 1 + FIREWIDTH / 8 + +; draw all fire cells +; args: pointer to fire cell buffer, screen x, screen y + .EQU FFD_CELL_PTR 0 + .EQU FFD_X 4 + .EQU FFD_Y 8 + .EQU FFD_ROW_COUNT 12 + .EQU FFD_ROW_WORDCOUNT 16 + .EQU FFD_VMEM_PTR 20 + .EQU FFD_FS 24 +FASTFIREDRAW: + FPADJ -FFD_FS + STORE FFD_Y + STORE FFD_X + STORE FFD_CELL_PTR + + ; calculate video memory addr + ; addr = y * 80 + X / 8 + LOAD FFD_Y + SHL 2 ; y * 16 + SHL 2 + DUP + SHL 2 ; + y * 64 + ADD ; = y * 80 + + LOAD FFD_X + SHR + SHR + SHR + ADD ; + x / 8 + + DUP + STORE FFD_VMEM_PTR + LOADC FB_WA ; set vmem write address + SWAP + STOREI + DROP + + LOADC FIREHEIGHT + 1 + STORE FFD_ROW_COUNT +FFD_ROW: + LOADC FFD_ROW_WORDS + STORE FFD_ROW_WORDCOUNT + + LOADC FB_WA ; set vmem write address + LOAD FFD_VMEM_PTR + STOREI + DROP + +FFD_WORD: + LOAD FFD_CELL_PTR ; load cell ptr + LOADC 0 ; vmem word, start with 0 + + ; leftmost pixel (0) + OVER ; [ cptr, vmemw, cptr ] + LOADI ; load cell value [ cptr, vmemw, cellval ] + SHR ; scale it down (from 7 bits to 4) + SHR + SHR ; [ cptr, vmemw, cellval shr 3 ] + OR ; [ cptr, vmemw ] + SWAP ; [ vmemw, cptr ] + INC 4 ; increment cell ptr on stack [ vmemw, cptr + 4 ] + SWAP ; [ cptr + 4, vmemw ] + + SHL 2 ; move bits to left for next pixel + SHL 2 + + ; pixel 1 + OVER + LOADI ; load cell value + SHR ; scale it down (from 7 bits to 4) + SHR + SHR + OR + SWAP + INC 4 ; increment cell ptr on stack + SWAP + + SHL 2 ; move bits to left for next pixel + SHL 2 + + ; pixel 2 + OVER + LOADI ; load cell value + SHR ; scale it down (from 7 bits to 4) + SHR + SHR + OR + SWAP + INC 4 ; increment cell ptr on stack + SWAP + + SHL 2 ; move bits to left for next pixel + SHL 2 + + ; pixel 3 + OVER + LOADI ; load cell value + SHR ; scale it down (from 7 bits to 4) + SHR + SHR + OR + SWAP + INC 4 ; increment cell ptr on stack + SWAP + + SHL 2 ; move bits to left for next pixel + SHL 2 + + ; pixel 4 + OVER + LOADI ; load cell value + SHR ; scale it down (from 7 bits to 4) + SHR + SHR + OR + SWAP + INC 4 ; increment cell ptr on stack + SWAP + + SHL 2 ; move bits to left for next pixel + SHL 2 + + ; pixel 5 + OVER + LOADI ; load cell value + SHR ; scale it down (from 7 bits to 4) + SHR + SHR + OR + SWAP + INC 4 ; increment cell ptr on stack + SWAP + + SHL 2 ; move bits to left for next pixel + SHL 2 + + ; pixel 6 + OVER + LOADI ; load cell value + SHR ; scale it down (from 7 bits to 4) + SHR + SHR + OR + SWAP + INC 4 ; increment cell ptr on stack + SWAP + + SHL 2 ; move bits to left for next pixel + SHL 2 + + ; pixel 7 + OVER + LOADI ; load cell value + SHR ; scale it down (from 7 bits to 4) + SHR + SHR + OR + SWAP + INC 4 ; increment cell ptr on stack + SWAP + + ; store word to vmem + ; vmem write addr will autoincrement + LOADC FB_IO + SWAP + STOREI + DROP + + STORE FFD_CELL_PTR + + ; prepare for next word + LOAD FFD_ROW_WORDCOUNT + DEC 1 + DUP + STORE FFD_ROW_WORDCOUNT + CBRANCH.NZ FFD_WORD + + ; prepare for next row + LOAD FFD_VMEM_PTR + LOADC WORDS_PER_LINE + ADD + STORE FFD_VMEM_PTR + + LOAD FFD_ROW_COUNT + DEC 1 + DUP + STORE FFD_ROW_COUNT + CBRANCH.NZ FFD_ROW +FFD_EXIT: + FPADJ FFD_FS + RET diff --git a/examples/fire.pas b/examples/fire.pas new file mode 100644 index 0000000..22f0217 --- /dev/null +++ b/examples/fire.pas @@ -0,0 +1,76 @@ +{$H1} +{$S2} +program fire; +const MAXX = 30; + MAXY = 50; +var firebuf: array [0..MAXY, 0..MAXX] of integer; + + firepalette: array [0..15] of integer = + ( $FFA, $FF8, $FF4, $FF0, $FE0, $FD0, $FA0, $F90, + $F00, $E00, $D00, $A00, $800, $600, $300, $000); + x,y:integer; + +procedure createPalette; +var i:integer; +begin + for i := 15 downto 0 do + setpalette(15 - i, firepalette[i]); +end; + +procedure fireItUp; +var x,y:integer; +begin + y := MAXY - 1; + for x := 1 to MAXX - 1 do + firebuf[y, x] := random and 127; +end; + +procedure updateFire; +var i,x,y:integer; +begin + for y := 0 to MAXY - 2 do + for x := 1 to MAXX - 1 do + begin + i := + ((firebuf[y + 1, x - 1] + + firebuf[y + 1, x] + + firebuf[y + 1, x + 1] + + firebuf[y + 2, x]) + ) shr 2; + if i > 0 then + i := i - 1; + firebuf[y, x] := i; + end; +end; + +procedure drawFire; +var x, y, col:integer; +begin + for y := 0 to MAXY - 1 do + begin + x := 0; + for col in firebuf[y] do + begin + putpixel(300 + x, 150 + y, col shr 3); + x := x + 1; + end; + end; +end; + +begin + randomize; + initgraphics; + createPalette; + while not conavail do + begin + fireItUp; + updateFire; + drawFire; + end; + + for y := 0 to MAXY do + begin + x := firebuf[y, 10]; + drawline(0, y, x, y, 1); + end; +end. diff --git a/examples/fire2.pas b/examples/fire2.pas new file mode 100644 index 0000000..72fb254 --- /dev/null +++ b/examples/fire2.pas @@ -0,0 +1,84 @@ +{$H1} +{$S1} +program fire2; +uses fastfire; + +const MAXX = FIREWIDTH; + MAXY = FIREHEIGHT; + +var firecells: FireBuf; + + firepalette: array [0..15] of integer = + { ( $FFA, $FF8, $FF4, $FF0, $FE0, $FD0, $FA0, $F90, + $F00, $E00, $D00, $A00, $800, $600, $300, $000); } + ( $FFA, $FFA, $FFA, $FFA, $FF0, $FF0, $FF0, $FF0, + $FF0, $FD0, $FA0, $C00, $A00, $700, $400, $000); + x,y:integer; + +procedure createPalette; +var i:integer; +begin + for i := 15 downto 0 do + setpalette(15 - i, firepalette[i]); +end; + +procedure fireItUp; +var x,y:integer; +begin + y := MAXY - 1; + for x := 1 to MAXX - 1 do + firecells[y, x] := random and 127; +end; + + +procedure updateFire; +var i,x,y:integer; +begin + for y := 0 to MAXY - 2 do + for x := 1 to MAXX - 1 do + begin + i := + ((firecells[y + 1, x - 1] + + firecells[y + 1, x] + + firecells[y + 1, x + 1] + + firecells[y + 2, x]) + ) shr 2; + if i > 0 then + i := i - 1; + firecells[y, x] := i; + end; +end; + +procedure drawFire; +var x, y, col:integer; +begin + for y := 0 to MAXY - 1 do + begin + x := 0; + for col in firecells[y] do + begin + putpixel(100 + x, 150 + y, col shr 3); + x := x + 1; + end; + end; +end; + +begin + randomize; + initgraphics; + createPalette; + while not conavail do + begin + fireItUp; + FastFireUpdate(firecells); + { updateFire; } + FastFireDraw(firecells, 160, 100); + { drawFire; } + end; + + for y := 0 to MAXY do + begin + x := firecells[y, 10]; + drawline(0, y, x, y, 1); + end; +end. diff --git a/pcomp/sasm.pas b/pcomp/sasm.pas index 2af55c3..d032748 100644 --- a/pcomp/sasm.pas +++ b/pcomp/sasm.pas @@ -2057,7 +2057,7 @@ begin emitBlock(count, operandValue); end else - if lastToken.tokenText6 = '.ALIGN' then + if lastToken.tokenText = '.ALIGN' then alignOutput(wordSize) else errorExit2('Unrecognized directive', lastToken.tokenText); From 0016d4ea25d95614417f1334c58fd9571052a809 Mon Sep 17 00:00:00 2001 From: slederer Date: Fri, 5 Dec 2025 00:58:15 +0100 Subject: [PATCH 10/11] utils/serload: add interactive mode xfer: reset block count on transfer start --- progs/xfer.pas | 1 + utils/serload.py | 151 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 110 insertions(+), 42 deletions(-) diff --git a/progs/xfer.pas b/progs/xfer.pas index 13a7cc2..0d871d2 100644 --- a/progs/xfer.pas +++ b/progs/xfer.pas @@ -226,6 +226,7 @@ begin if not invalid then begin open(xferFile, filename, ModeOverwrite); + blockNo := 0; done := false; repeat serReadBlock(ok); diff --git a/utils/serload.py b/utils/serload.py index 6ccc4a6..e69837f 100644 --- a/utils/serload.py +++ b/utils/serload.py @@ -16,6 +16,7 @@ # limitations under the License. import sys +import os import serial import time import random @@ -41,30 +42,6 @@ def get_default_device(): return '/dev/ttyUSB1' -def serwrite_slow(databytes, ser): - total = len(data) - count = 1 - for d in data: - sys.stdout.write("writing {0:02x} {1:04d}/{2:04d}\r".format(ord(d), count, total)) - ser.write(bytes(d,"utf8")) - count += 1 - time.sleep(0.020) - print() - - -def serwrite(datafile, ser): - with open(datafile) as f: - data = f.read() - total = len(data) - count = 1 - for d in data: - sys.stdout.write("writing {0:02x} {1:04d}/{2:04d}\r".format(ord(d), count, total)) - ser.write(bytes(d,"utf8")) - count += 1 - time.sleep(0.020) - print() - - def checksum(databytes): i = 0 cksum = 0 @@ -85,10 +62,26 @@ def sendchar(char, ser): ser.write(char.to_bytes(1, 'big')) -def sendcommand(ser, cmd=b'L'): +def sendcommand(ser, cmd=b'L', verbose=False): + verbose = True ser.write(cmd) resp = ser.read_until() - print(cmd,"sent, response:", str(resp)) + if verbose: + print(cmd,"sent, response:", str(resp)) + return resp + + +# send command and wait for echo +def commandwait(ser, cmd): + resp = sendcommand(ser, cmd, verbose=False) + if len(resp) == 0: + print("timeout sending '{}' command".format(cmd)) + return None + + if resp != bytearray(cmd + b"\r\n"): + print("invalid response to '{}' command".format(cmd)) + return None + return resp @@ -153,6 +146,8 @@ def serload_bin(datafile, ser): data += bytearray(pad) + print("{} total blocks".format((len(data) + blocksize - 1) // blocksize)) + if not send_size_header(ser, filesize): print("Error sending size header.") return @@ -279,18 +274,8 @@ def serdownload(fname, ser): def mput(filenames, ser): for f in filenames: - f_encoded = f.encode('utf8') - print("Setting filename", f) - resp = sendcommand(ser, b'S') - if len(resp) == 0: - print("timeout sending 'S' command") - return - if resp != b'S\r\n' and resp != b'> S\r\n': - print("unrecognized response to 'S' command, aborting") - return - resp = sendcommand(ser, f_encoded + b'\r') - if not f_encoded in resp: - print("unrecognized response to filename, aborting") + resp = set_filename(f, ser) + if resp is None: return serload_bin(f, ser) @@ -299,12 +284,92 @@ def mput(filenames, ser): time.sleep(2) +def set_filename(f, ser): + f_encoded = f.encode('utf8') + print("Setting filename", f) + resp = commandwait(ser, b'S') + if resp is None: + return None + resp = sendcommand(ser, f_encoded + b'\r') + if not f_encoded in resp: + print("unrecognized response to filename, aborting") + return None + return resp + + +def getnamedfile(filename, ser): + resp = set_filename(filename, ser) + if resp is None: + return None + serdownload(filename, ser) + + +def putnamedfile(filename, ser): + resp = set_filename(filename, ser) + if resp is None: + return None + serload_bin(filename, ser) + print("Remote status:") + showdata(ser) + + +def showdata(ser): + + promptseen = False + + while not promptseen: + c = ser.read(1) + if c == b'>': + promptseen = True + else: + print(c.decode('utf8'), end='') + rest = ser.read(1) + + +def localdir(): + result = os.walk(".") + for dirpath, dirnames, filenames in os.walk("."): + for f in filenames: + print(f) + break + + +def interactive(ser): + done = False + while not done: + args = input("> ").strip().split() + if len(args) > 0: + cmd = args[0] + args.pop(0) + if cmd == 'dir': + if commandwait(ser, b'Y') is None: + return + showdata(ser) + elif cmd == 'get': + if len(args) > 1: + print("exactly one argument required (filename)") + else: + getnamedfile(args[0], ser) + elif cmd == 'put': + if len(args) > 1: + print("exactly one argument required (filename)") + else: + putnamedfile(args[0], ser) + elif cmd == 'ldir': + if len(args) > 0: + print("superfluous argument") + else: + localdir() + else: + print("Unknown command. Valid commands are: dir get ldir put") + + if __name__ == "__main__": argparser = argparse.ArgumentParser( description='transfer files from/to the Tridora-CPU') argparser.add_argument('-d', '--device', help='serial device', default=get_default_device()) - argparser.add_argument('command', choices=['get', 'put', 'mput']) - argparser.add_argument('filename', nargs='+') + argparser.add_argument('command', choices=['get', 'put', 'mput', 'interactive']) + argparser.add_argument('filename', nargs='*') args = argparser.parse_args() cmd = args.command @@ -319,8 +384,10 @@ if __name__ == "__main__": serload_bin(filenames[0], ser) elif cmd == 'mput': mput(filenames, ser) + elif cmd == 'interactive': + interactive(ser) else: print("should not get here") - if cmd is not None: - ser.close() + #if cmd is not None: + # ser.close() From d2f3b09e72e1990dc38c5c643b9cf7446c0f70b7 Mon Sep 17 00:00:00 2001 From: slederer Date: Mon, 15 Dec 2025 00:53:36 +0100 Subject: [PATCH 11/11] tridoracpu: cleaned up top a bit, removed some warnings --- .../tridoracpu.srcs/Arty-A7-35-Master.xdc | 6 ++-- tridoracpu/tridoracpu.srcs/stackcpu.v | 23 +++++-------- tridoracpu/tridoracpu.srcs/tdraudio.v | 22 +++++++++---- tridoracpu/tridoracpu.srcs/top.v | 21 ++++++------ tridoracpu/tridoracpu.xpr | 33 +++++++------------ 5 files changed, 47 insertions(+), 58 deletions(-) diff --git a/tridoracpu/tridoracpu.srcs/Arty-A7-35-Master.xdc b/tridoracpu/tridoracpu.srcs/Arty-A7-35-Master.xdc index 2a33ae0..d2c3160 100644 --- a/tridoracpu/tridoracpu.srcs/Arty-A7-35-Master.xdc +++ b/tridoracpu/tridoracpu.srcs/Arty-A7-35-Master.xdc @@ -8,8 +8,8 @@ set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33} [get_ports clk] create_clock -period 10.000 -name sys_clk_pin -waveform {0.000 5.000} -add [get_ports clk] ## Switches -set_property -dict {PACKAGE_PIN A8 IOSTANDARD LVCMOS33} [get_ports sw0] -set_property -dict {PACKAGE_PIN C11 IOSTANDARD LVCMOS33} [get_ports sw1] +#set_property -dict {PACKAGE_PIN A8 IOSTANDARD LVCMOS33} [get_ports sw0] +#set_property -dict {PACKAGE_PIN C11 IOSTANDARD LVCMOS33} [get_ports sw1] #set_property -dict { PACKAGE_PIN C10 IOSTANDARD LVCMOS33 } [get_ports { sw[2] }]; #IO_L13N_T2_MRCC_16 Sch=sw[2] #set_property -dict { PACKAGE_PIN A10 IOSTANDARD LVCMOS33 } [get_ports { sw[3] }]; #IO_L14P_T2_SRCC_16 Sch=sw[3] @@ -34,7 +34,7 @@ set_property -dict {PACKAGE_PIN T9 IOSTANDARD LVCMOS33} [get_ports led2] set_property -dict {PACKAGE_PIN T10 IOSTANDARD LVCMOS33} [get_ports led3] ## Buttons -set_property -dict {PACKAGE_PIN D9 IOSTANDARD LVCMOS33} [get_ports btn0] +#set_property -dict {PACKAGE_PIN D9 IOSTANDARD LVCMOS33} [get_ports btn0] #set_property -dict { PACKAGE_PIN C9 IOSTANDARD LVCMOS33 } [get_ports { btn1 }]; #IO_L11P_T1_SRCC_16 Sch=btn[1] #set_property -dict { PACKAGE_PIN B9 IOSTANDARD LVCMOS33 } [get_ports { btn[2] }]; #IO_L11N_T1_SRCC_16 Sch=btn[2] #set_property -dict { PACKAGE_PIN B8 IOSTANDARD LVCMOS33 } [get_ports { btn[3] }]; #IO_L12P_T1_MRCC_16 Sch=btn[3] diff --git a/tridoracpu/tridoracpu.srcs/stackcpu.v b/tridoracpu/tridoracpu.srcs/stackcpu.v index 33b58ec..1d929f7 100644 --- a/tridoracpu/tridoracpu.srcs/stackcpu.v +++ b/tridoracpu/tridoracpu.srcs/stackcpu.v @@ -16,11 +16,11 @@ module stackcpu #(parameter ADDR_WIDTH = 32, WIDTH = 32, output wire write_enable, input wire mem_wait, - output wire led1, - output wire led2, - output wire led3 + output wire debug1, + output wire debug2, + output wire debug3 ); - + localparam EVAL_STACK_INDEX_WIDTH = 6; wire reset = !rst; @@ -90,7 +90,6 @@ module stackcpu #(parameter ADDR_WIDTH = 32, WIDTH = 32, wire mem_write; wire x_is_zero; - // wire [WIDTH-1:0] y_plus_operand = Y + operand; wire x_equals_y = X == Y; wire y_lessthan_x = $signed(Y) < $signed(X); @@ -105,16 +104,10 @@ module stackcpu #(parameter ADDR_WIDTH = 32, WIDTH = 32, assign write_enable = mem_write_enable; // debug output ------------------------------------------------------------------------------------ - assign led1 = reset; - assign led2 = ins_loadc; - assign led3 = ins_branch; -// assign debug_out1 = { mem_read_enable, mem_write_enable, x_is_zero, -// ins_branch, ins_aluop, y_lessthan_x, x_equals_y, {7{1'b0}}, seq_state}; -// assign debug_out2 = data_in; -// assign debug_out3 = nX; -// assign debug_out4 = nPC; -// assign debug_out5 = ins; -// assign debug_out6 = IV; + assign debug1 = reset; + assign debug2 = ins_loadc; + assign debug3 = ins_branch; + //-------------------------------------------------------------------------------------------------- // instruction decoding diff --git a/tridoracpu/tridoracpu.srcs/tdraudio.v b/tridoracpu/tridoracpu.srcs/tdraudio.v index 0cc055e..1629e31 100644 --- a/tridoracpu/tridoracpu.srcs/tdraudio.v +++ b/tridoracpu/tridoracpu.srcs/tdraudio.v @@ -7,7 +7,7 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, input wire reset, input wire [1:0] reg_sel, output wire [DATA_WIDTH-1:0] rd_data, - input wire [DATA_WIDTH-1:0] wr_data, + input wire [AMP_WIDTH-1:0] wr_data, input wire rd_en, input wire wr_en, @@ -20,6 +20,9 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, localparam TDRAU_REG_CLK = 1; /* clock divider register */ localparam TDRAU_REG_AMP = 2; /* amplitude (volume) register */ + /* avoid warning about unconnected port */ + (* keep="soft" *) wire _unused = rd_en; + reg channel_enable; reg [CLOCK_DIV_WIDTH-1:0] clock_div; reg [CLOCK_DIV_WIDTH-1:0] div_count; @@ -29,12 +32,12 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, wire fifo_wr_en; wire fifo_rd_en, fifo_full, fifo_empty; - wire [DATA_WIDTH-1:0] fifo_rd_data; + wire [AMP_WIDTH-1:0] fifo_rd_data; fifo #(.ADDR_WIDTH(4), .DATA_WIDTH(16)) sample_buf( clk, reset, fifo_wr_en, fifo_rd_en, - wr_data, fifo_rd_data, + wr_data[AMP_WIDTH-1:0], fifo_rd_data, fifo_full, fifo_empty ); @@ -166,9 +169,14 @@ module tdraudio #(DATA_WIDTH=32) ( localparam AMP_BIAS = 32768; localparam DAC_WIDTH = 18; + /* avoid warning about unconnected port */ + (* keep="soft" *) wire [DATA_WIDTH-1:AMP_WIDTH] _unused = wr_data[DATA_WIDTH-1:AMP_WIDTH]; + wire [4:0] chan_sel = io_addr[6:2]; wire [1:0] reg_sel = io_addr[1:0]; + wire [AMP_WIDTH-1:0] amp_wr_data = wr_data[AMP_WIDTH-1:0]; + wire [AMP_WIDTH-1:0] chan0_amp; wire [DATA_WIDTH-1:0] chan0_rd_data; wire chan0_running; @@ -210,25 +218,25 @@ module tdraudio #(DATA_WIDTH=32) ( {DATA_WIDTH{1'b1}}; wavegen chan0(clk, reset, reg_sel, - chan0_rd_data, wr_data, + chan0_rd_data, amp_wr_data, chan0_rd_en, chan0_wr_en, chan0_amp, chan0_running, chan0_irq); wavegen chan1(clk, reset, reg_sel, - chan1_rd_data, wr_data, + chan1_rd_data, amp_wr_data, chan1_rd_en, chan1_wr_en, chan1_amp, chan1_running, chan1_irq); wavegen chan2(clk, reset, reg_sel, - chan2_rd_data, wr_data, + chan2_rd_data, amp_wr_data, chan2_rd_en, chan2_wr_en, chan2_amp, chan2_irq, chan2_running); wavegen chan3(clk, reset, reg_sel, - chan3_rd_data, wr_data, + chan3_rd_data, amp_wr_data, chan3_rd_en, chan3_wr_en, chan3_amp, chan3_running, chan3_irq); diff --git a/tridoracpu/tridoracpu.srcs/top.v b/tridoracpu/tridoracpu.srcs/top.v index 6a70ef0..a4533d2 100644 --- a/tridoracpu/tridoracpu.srcs/top.v +++ b/tridoracpu/tridoracpu.srcs/top.v @@ -15,9 +15,6 @@ module top( input wire clk, input wire rst, - input wire btn0, - input wire sw0, - input wire sw1, output wire led0, output wire led1, output wire led2, @@ -229,6 +226,15 @@ module top( assign uart_rd_data = { {WIDTH-10{1'b1}}, uart_rx_avail, uart_tx_busy, uart_rx_data }; wire audio_irq; + + buart #(.CLKFREQ(`clkfreq)) uart0(`clock, rst, + uart_baud, + uart_txd_in, uart_rxd_out, + uart_rx_clear, uart_tx_en, + uart_rx_avail, uart_tx_busy, + uart_tx_data, uart_rx_data); + + // audio controller `ifdef ENABLE_TDRAUDIO wire [WIDTH-1:0] tdraudio_wr_data; wire [WIDTH-1:0] tdraudio_rd_data; @@ -273,13 +279,6 @@ module top( `endif -1; - buart #(.CLKFREQ(`clkfreq)) uart0(`clock, rst, - uart_baud, - uart_txd_in, uart_rxd_out, - uart_rx_clear, uart_tx_en, - uart_rx_avail, uart_tx_busy, - uart_tx_data, uart_rx_data); - // CPU ----------------------------------------------------------------- stackcpu cpu0(.clk(`clock), .rst(rst), .irq(irq), .addr(mem_addr), @@ -287,7 +286,7 @@ module top( .read_ins(dram_read_ins), .data_out(mem_write_data), .write_enable(mem_write_enable), .mem_wait(mem_wait), - .led1(led1), .led2(led2), .led3(led3)); + .debug1(led1), .debug2(led2), .debug3(led3)); // Interrupt Controller irqctrl irqctrl0(`clock, irq_in, irqc_cs, mem_write_enable, diff --git a/tridoracpu/tridoracpu.xpr b/tridoracpu/tridoracpu.xpr index 30d168a..a9dc20f 100644 --- a/tridoracpu/tridoracpu.xpr +++ b/tridoracpu/tridoracpu.xpr @@ -356,15 +356,12 @@ - + - - Performs general area optimizations including changing the threshold for control set optimizations, forcing ternary adder implementation, applying lower thresholds for use of carry chain in comparators and also area optimized mux optimizations. + + Vivado Synthesis Defaults - - - - + @@ -381,26 +378,18 @@ - + - - Uses multiple algorithms for optimization, placement, and routing to get potentially better results. + + Default settings for Implementation. - - - + - - - + - - - - - - + +