diff --git a/dist/Cores/ericlewis.SpaceRace/bitstream.rbf_r b/dist/Cores/ericlewis.SpaceRace/bitstream.rbf_r index cf3e62e..7e9c71c 100644 Binary files a/dist/Cores/ericlewis.SpaceRace/bitstream.rbf_r and b/dist/Cores/ericlewis.SpaceRace/bitstream.rbf_r differ diff --git a/dist/Cores/ericlewis.SpaceRace/interact.json b/dist/Cores/ericlewis.SpaceRace/interact.json index 1ead740..a18db84 100644 --- a/dist/Cores/ericlewis.SpaceRace/interact.json +++ b/dist/Cores/ericlewis.SpaceRace/interact.json @@ -3,46 +3,30 @@ "magic": "APF_VER_1", "variables": [ { - "id": 1, - "name": "Use Ctrl 2 for P2", + "name": "2 Credits Per Coin", + "id": 2, "type": "check", "enabled": true, "persist": true, "writeonly": true, - "address": "0x00300000", + "address": "0x00400000", "defaultval": 0, "value": 1 }, { - "name": "Credit Per Coin", - "id": 2, - "type": "slider_u32", - "enabled": true, - "persist": true, - "writeonly": true, - "address": "0x00400000", - "defaultval": 2, - "graphical": { - "min": 1, - "max": 2, - "adjust_small": 1, - "adjust_large": 1 - } - }, - { - "name": "Playtime [%]", + "name": "Playtime", "id": 3, "type": "slider_u32", "enabled": true, "persist": true, "writeonly": true, "address": "0x00500000", - "defaultval": 100, + "defaultval": 10, "graphical": { "min": 0, - "max": 100, - "adjust_small": 10, - "adjust_large": 20 + "max": 10, + "adjust_small": 1, + "adjust_large": 2 } } ], diff --git a/src/fpga/ap_core.qsf b/src/fpga/ap_core.qsf index 8533dca..ef09c96 100644 --- a/src/fpga/ap_core.qsf +++ b/src/fpga/ap_core.qsf @@ -737,6 +737,7 @@ set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top # end ENTITY(mf_pllbase_0002) # --------------------------- +set_global_assignment -name SYSTEMVERILOG_FILE core/sync_fifo.sv set_global_assignment -name SYSTEMVERILOG_FILE core/sound_i2s.sv set_global_assignment -name SYSTEMVERILOG_FILE core/core_top.sv set_global_assignment -name SYSTEMVERILOG_FILE core/rtl/videosync.sv diff --git a/src/fpga/apf/apf_top.v b/src/fpga/apf/apf_top.v index e82f37b..59bfdbb 100644 --- a/src/fpga/apf/apf_top.v +++ b/src/fpga/apf/apf_top.v @@ -280,10 +280,10 @@ mf_ddio_bidir_12 isclk( // controller data (pad) controller. - wire [15:0] cont1_key; - wire [15:0] cont2_key; - wire [15:0] cont3_key; - wire [15:0] cont4_key; + wire [31:0] cont1_key; + wire [31:0] cont2_key; + wire [31:0] cont3_key; + wire [31:0] cont4_key; wire [31:0] cont1_joy; wire [31:0] cont2_joy; wire [31:0] cont3_joy; @@ -472,4 +472,3 @@ core_top ic ( ); endmodule - diff --git a/src/fpga/apf/build_id.mif b/src/fpga/apf/build_id.mif index 1ac0c07..a694954 100644 --- a/src/fpga/apf/build_id.mif +++ b/src/fpga/apf/build_id.mif @@ -9,8 +9,8 @@ DATA_RADIX = HEX; CONTENT BEGIN - 0E0 : 20220916; - 0E1 : 00080124; - 0E2 : a7ddf1b2; + 0E0 : 20221011; + 0E1 : 00125820; + 0E2 : 0f5bd7d6; END; diff --git a/src/fpga/apf/common.v b/src/fpga/apf/common.v index 6481788..1bb3b60 100644 --- a/src/fpga/apf/common.v +++ b/src/fpga/apf/common.v @@ -153,4 +153,4 @@ always @(posedge b_clk) begin b_dout <= mem[b_addr]; end -endmodule +endmodule \ No newline at end of file diff --git a/src/fpga/apf/io_bridge_peripheral.v b/src/fpga/apf/io_bridge_peripheral.v index eda3bb0..0cda7fe 100644 --- a/src/fpga/apf/io_bridge_peripheral.v +++ b/src/fpga/apf/io_bridge_peripheral.v @@ -331,4 +331,4 @@ always @(posedge phy_spiclk or posedge phy_spiss) begin end end -endmodule +endmodule \ No newline at end of file diff --git a/src/fpga/apf/io_pad_controller.v b/src/fpga/apf/io_pad_controller.v index c3886ac..81a62f6 100644 --- a/src/fpga/apf/io_pad_controller.v +++ b/src/fpga/apf/io_pad_controller.v @@ -45,10 +45,10 @@ input wire reset_n, inout reg pad_1wire, -output reg [15:0] cont1_key, -output reg [15:0] cont2_key, -output reg [15:0] cont3_key, -output reg [15:0] cont4_key, +output reg [31:0] cont1_key, +output reg [31:0] cont2_key, +output reg [31:0] cont3_key, +output reg [31:0] cont4_key, output reg [31:0] cont1_joy, output reg [31:0] cont2_joy, output reg [31:0] cont3_joy, @@ -140,19 +140,19 @@ always @(posedge clk) begin if(rx_word_done) begin cnt <= cnt + 1'b1; case(cnt) - 0: cont1_key <= rx_word[15:0]; + 0: cont1_key <= rx_word; 1: cont1_joy <= rx_word; 2: cont1_trig <= rx_word[15:0]; - 3: cont2_key <= rx_word[15:0]; + 3: cont2_key <= rx_word; 4: cont2_joy <= rx_word; 5: cont2_trig <= rx_word[15:0]; - 6: cont3_key <= rx_word[15:0]; + 6: cont3_key <= rx_word; 7: cont3_joy <= rx_word; 8: cont3_trig <= rx_word[15:0]; - 9: cont4_key <= rx_word[15:0]; + 9: cont4_key <= rx_word; 10: cont4_joy <= rx_word; 11: begin cont4_trig <= rx_word[15:0]; @@ -325,4 +325,4 @@ always @(posedge clk) begin if(~reset_n_s | ~reset_tr_n) tr_state <= TR_IDLE; end -endmodule +endmodule \ No newline at end of file diff --git a/src/fpga/core/core_top.sv b/src/fpga/core/core_top.sv index 0010c90..5ab1690 100644 --- a/src/fpga/core/core_top.sv +++ b/src/fpga/core/core_top.sv @@ -209,10 +209,10 @@ input wire [31:0] bridge_wr_data, // [ 7: 0] ltrig // [15: 8] rtrig // -input wire [15:0] cont1_key, -input wire [15:0] cont2_key, -input wire [15:0] cont3_key, -input wire [15:0] cont4_key, +input wire [31:0] cont1_key, +input wire [31:0] cont2_key, +input wire [31:0] cont3_key, +input wire [31:0] cont4_key, input wire [31:0] cont1_joy, input wire [31:0] cont2_joy, input wire [31:0] cont3_joy, @@ -461,7 +461,7 @@ mf_pllbase mp1 ( // Core Settings //////////////////////////////////////////// -reg disable_p2_on_pad_1 = 0; +wire disable_p2_on_pad_1 = cont1_key_s[31:29]; reg COINAGE = 1'b0; reg [3:0] PLAYTIME = 4'd10; @@ -469,11 +469,8 @@ always @(posedge clk_74a) begin if(bridge_wr) begin casex(bridge_addr) - 32'h00300000: begin - disable_p2_on_pad_1 <= bridge_wr_data[0:0]; - end 32'h00400000: begin - COINAGE <= bridge_wr_data[0:0]; + COINAGE <= bridge_wr_data[0]; end 32'h00500000: begin PLAYTIME <= bridge_wr_data[3:0]; @@ -494,9 +491,6 @@ wire HSYNC, VSYNC, HBLANK, VBLANK; wire [7:0] video = (VIDEO ? 8'd255 : (SCORE ? 8'd187 : 8'd0)); -// Does not do anything - just to satisfy the top. -wire CLK_CORE_VIDEO; - // // Video cleanup // APF scaler requires HSync and VSync to last for a single clock, and video_rgb to be 0 when video_de is low @@ -538,72 +532,30 @@ end wire [15:0] SOUND; -assign audio_mclk = audgen_mclk; -assign audio_dac = audgen_dac; -assign audio_lrck = audgen_lrck; - -reg audgen_nextsamp; - -// generate MCLK = 12.288mhz with fractional accumulator -reg [21:0] audgen_accum; -reg audgen_mclk; -parameter [20:0] CYCLE_48KHZ = 21'd122880 * 2; -always @(posedge clk_74a) -begin - audgen_accum <= audgen_accum + CYCLE_48KHZ; - if(audgen_accum >= 21'd742500) begin - audgen_mclk <= ~audgen_mclk; - audgen_accum <= audgen_accum - 21'd742500 + CYCLE_48KHZ; - end -end - -// generate SCLK = 3.072mhz by dividing MCLK by 4 -reg [1:0] aud_mclk_divider; -wire audgen_sclk = aud_mclk_divider[1] /* synthesis keep*/; -always @(posedge audgen_mclk) begin - aud_mclk_divider <= aud_mclk_divider + 1'b1; -end - -// shift out audio data as I2S -// 32 total bits per channel, but only 16 active bits at the start and then 16 dummy bits -// -// synchronize audio samples coming from the core -wire [31:0] audgen_sampdata_s; -synch_3 #(.WIDTH(32)) s5({SOUND, SOUND}, audgen_sampdata_s, audgen_sclk); -reg [31:0] audgen_sampshift; -reg [4:0] audgen_lrck_cnt; -reg audgen_lrck; -reg audgen_dac; -always @(negedge audgen_sclk) begin - // output the next bit - audgen_dac <= audgen_sampshift[31]; - - // 48khz * 64 - audgen_lrck_cnt <= audgen_lrck_cnt + 1'b1; - if(audgen_lrck_cnt == 31) begin - // switch channels - audgen_lrck <= ~audgen_lrck; - - // Reload sample shifter - if(~audgen_lrck) begin - audgen_sampshift <= audgen_sampdata_s; - end - end else if(audgen_lrck_cnt < 16) begin - // only shift for 16 clocks per channel - audgen_sampshift <= {audgen_sampshift[30:0], 1'b0}; - end -end +sound_i2s #( + .CHANNEL_WIDTH(16), + .SIGNED_INPUT (1) +) sound_i2s ( + .clk_74a(clk_74a), + .clk_audio(clk_sys), + + .audio_l(SOUND), + .audio_r(SOUND), + .audio_mclk(audio_mclk), + .audio_lrck(audio_lrck), + .audio_dac(audio_dac) +); //////////////////////////////////////////// // Core Controls //////////////////////////////////////////// -wire [15:0] cont1_key_s; -wire [15:0] cont2_key_s; +wire [31:0] cont1_key_s; +wire [31:0] cont2_key_s; synch_2 #( - .WIDTH(16) + .WIDTH(32) ) cont1_s ( cont1_key, cont1_key_s, @@ -611,7 +563,7 @@ synch_2 #( ); synch_2 #( - .WIDTH(16) + .WIDTH(32) ) cont2_s ( cont2_key, cont2_key_s, @@ -666,7 +618,6 @@ space_race_top space_race_top( .START_GAME, .UP1_N, .DOWN1_N, .UP2_N, .DOWN2_N, - .CLK_VIDEO(CLK_CORE_VIDEO), .VIDEO, .SCORE, .HSYNC, .VSYNC, .HBLANK, .VBLANK, diff --git a/src/fpga/core/sound_i2s.sv b/src/fpga/core/sound_i2s.sv index 7b63415..28e4814 100644 --- a/src/fpga/core/sound_i2s.sv +++ b/src/fpga/core/sound_i2s.sv @@ -28,14 +28,15 @@ module sound_i2s #( parameter SIGNED_INPUT = 0 ) ( input wire clk_74a, + input wire clk_audio, // Left and right audio channels. Can be in an arbitrary clock domain input wire [CHANNEL_WIDTH - 1:0] audio_l, input wire [CHANNEL_WIDTH - 1:0] audio_r, - output reg audgen_mclk, - output reg audgen_lrck, - output reg audgen_dac + output reg audio_mclk, + output reg audio_lrck, + output reg audio_dac ); // // audio i2s generator @@ -44,21 +45,27 @@ module sound_i2s #( reg audgen_nextsamp; // generate MCLK = 12.288mhz with fractional accumulator - reg [21:0] audgen_accum; + reg [21:0] audgen_accum = 0; parameter [20:0] CYCLE_48KHZ = 21'd122880 * 2; always @(posedge clk_74a) begin audgen_accum <= audgen_accum + CYCLE_48KHZ; if (audgen_accum >= 21'd742500) begin - audgen_mclk <= ~audgen_mclk; + audio_mclk <= ~audio_mclk; audgen_accum <= audgen_accum - 21'd742500 + CYCLE_48KHZ; end end // generate SCLK = 3.072mhz by dividing MCLK by 4 reg [1:0] aud_mclk_divider; + reg prev_audio_mclk; wire audgen_sclk = aud_mclk_divider[1] /* synthesis keep*/; - always @(posedge audgen_mclk) begin - aud_mclk_divider <= aud_mclk_divider + 1'b1; + + always @(posedge clk_74a) begin + if (audio_mclk && ~prev_audio_mclk) begin + aud_mclk_divider <= aud_mclk_divider + 1'b1; + end + + prev_audio_mclk <= audio_mclk; end // shift out audio data as I2S @@ -66,52 +73,86 @@ module sound_i2s #( // // synchronize audio samples coming from the core - localparam CHANNEL_RIGHT_HIGH = SIGNED_INPUT ? 16 : 15; - localparam CHANNEL_LEFT_HIGH = 16 + CHANNEL_RIGHT_HIGH; + localparam CHANNEL_LEFT_HIGH = SIGNED_INPUT ? 16 : 15; + localparam CHANNEL_RIGHT_HIGH = 16 + CHANNEL_LEFT_HIGH; + + // Width of channel with signed component + localparam SIGNED_CHANNEL_WIDTH = SIGNED_INPUT ? CHANNEL_WIDTH : CHANNEL_WIDTH + 1; wire [31:0] audgen_sampdata; - assign audgen_sampdata[CHANNEL_LEFT_HIGH-1:CHANNEL_LEFT_HIGH-CHANNEL_WIDTH] = audio_l; - assign audgen_sampdata[31-CHANNEL_WIDTH:16] = 0; + assign audgen_sampdata[CHANNEL_LEFT_HIGH-1:CHANNEL_LEFT_HIGH-CHANNEL_WIDTH] = audio_l; assign audgen_sampdata[CHANNEL_RIGHT_HIGH-1:CHANNEL_RIGHT_HIGH-CHANNEL_WIDTH] = audio_r; - assign audgen_sampdata[15-CHANNEL_WIDTH:0] = 0; generate - if (~SIGNED_INPUT) begin + if (!SIGNED_INPUT) begin // If not signed, make sure high bit is 0 assign audgen_sampdata[31] = 0; assign audgen_sampdata[15] = 0; end endgenerate - wire [31:0] audgen_sampdata_s; - synch_3 #( + generate + if (15 - SIGNED_CHANNEL_WIDTH > 0) begin + assign audgen_sampdata[31-SIGNED_CHANNEL_WIDTH:16] = 0; + assign audgen_sampdata[15-SIGNED_CHANNEL_WIDTH:0] = 0; + end + endgenerate + + sync_fifo #( .WIDTH(32) - ) s5 ( - audgen_sampdata, - audgen_sampdata_s, - audgen_sclk + ) sync_fifo ( + .clk_write(clk_audio), + .clk_read (clk_74a), + + .write_en(write_en), + .data_in (audgen_sampdata), + .data_out(audgen_sampdata_s) ); + + reg write_en = 0; + reg [CHANNEL_WIDTH - 1:0] prev_left; + reg [CHANNEL_WIDTH - 1:0] prev_right; + + // Mark write when necessary + always @(posedge clk_audio) begin + prev_left <= audio_l; + prev_right <= audio_r; + + write_en <= 0; + + if (audio_l != prev_left || audio_r != prev_right) begin + write_en <= 1; + end + end + + wire [31:0] audgen_sampdata_s; + reg [31:0] audgen_sampshift; - reg [ 4:0] audgen_lrck_cnt; - always @(negedge audgen_sclk) begin - // output the next bit - audgen_dac <= audgen_sampshift[31]; - - // 48khz * 64 - audgen_lrck_cnt <= audgen_lrck_cnt + 1'b1; - if (audgen_lrck_cnt == 31) begin - // switch channels - audgen_lrck <= ~audgen_lrck; - - // Reload sample shifter - if (~audgen_lrck) begin - audgen_sampshift <= audgen_sampdata_s; + reg [4:0] audio_lrck_cnt; + reg prev_audgen_sclk; + always @(posedge clk_74a) begin + if (prev_audgen_sclk && ~audgen_sclk) begin + // output the next bit + audio_dac <= audgen_sampshift[31]; + + // 48khz * 64 + audio_lrck_cnt <= audio_lrck_cnt + 1'b1; + if (audio_lrck_cnt == 31) begin + // switch channels + audio_lrck <= ~audio_lrck; + + // Reload sample shifter + if (~audio_lrck) begin + audgen_sampshift <= audgen_sampdata_s; + end + end else if (audio_lrck_cnt < 16) begin + // only shift for 16 clocks per channel + audgen_sampshift <= {audgen_sampshift[30:0], 1'b0}; end - end else if (audgen_lrck_cnt < 16) begin - // only shift for 16 clocks per channel - audgen_sampshift <= {audgen_sampshift[30:0], 1'b0}; end + + prev_audgen_sclk <= audgen_sclk; end initial begin diff --git a/src/fpga/core/sync_fifo.sv b/src/fpga/core/sync_fifo.sv new file mode 100644 index 0000000..a41f0ef --- /dev/null +++ b/src/fpga/core/sync_fifo.sv @@ -0,0 +1,91 @@ +// MIT License + +// Copyright (c) 2022 Adam Gastineau + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//////////////////////////////////////////////////////////////////////////////// + +// An easily reusable method for synchronizing multiple bits across clock domains +// Uses a shallow depth (4 entries) FIFO, so make sure to empty it quickly +module sync_fifo #( + parameter WIDTH = 2 +) ( + input wire clk_write, + input wire clk_read, + + input wire write_en, + input wire [WIDTH - 1:0] data_in, + output reg [WIDTH - 1:0] data_out = 0 +); + + reg read_req = 0; + wire empty; + + wire [WIDTH - 1:0] fifo_out; + + dcfifo dcfifo_component ( + .data(data_in), + .rdclk(clk_read), + .rdreq(read_req), + .wrclk(clk_write), + .wrreq(write_en), + .q(fifo_out), + .rdempty(empty), + .aclr(), + .eccstatus(), + .rdfull(), + .rdusedw(), + .wrempty(), + .wrfull(), + .wrusedw() + ); + defparam dcfifo_component.intended_device_family = "Cyclone V", dcfifo_component.lpm_numwords = 4, + dcfifo_component.lpm_showahead = "OFF", dcfifo_component.lpm_type = "dcfifo", + dcfifo_component.lpm_width = 32, dcfifo_component.lpm_widthu = 2, + dcfifo_component.overflow_checking = "ON", dcfifo_component.rdsync_delaypipe = 5, + dcfifo_component.underflow_checking = "ON", dcfifo_component.use_eab = "ON", + dcfifo_component.wrsync_delaypipe = 5; + + reg [1:0] read_state = 0; + + localparam READ_DELAY = 1; + localparam READ_WRITE = 2; + + always @(posedge clk_read) begin + read_req <= 0; + + if (~empty) begin + read_state <= READ_DELAY; + read_req <= 1; + end + + case (read_state) + READ_DELAY: begin + read_state <= READ_WRITE; + end + READ_WRITE: begin + read_state <= 0; + + data_out <= fifo_out; + end + endcase + end + +endmodule diff --git a/src/fpga/output_files/ap_core.rbf b/src/fpga/output_files/ap_core.rbf index 9b1358a..282f678 100644 Binary files a/src/fpga/output_files/ap_core.rbf and b/src/fpga/output_files/ap_core.rbf differ diff --git a/src/fpga/output_files/ap_core.sof b/src/fpga/output_files/ap_core.sof index a5d6dcf..477187b 100644 Binary files a/src/fpga/output_files/ap_core.sof and b/src/fpga/output_files/ap_core.sof differ diff --git a/src/fpga/output_files/bitstream.rbf_r b/src/fpga/output_files/bitstream.rbf_r index cf3e62e..7e9c71c 100644 Binary files a/src/fpga/output_files/bitstream.rbf_r and b/src/fpga/output_files/bitstream.rbf_r differ