diff --git a/CHANGELOG.md b/CHANGELOG.md index 8778ddf..37c6ace 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changes +[25.06.2024] +- Add APU enhancement on top of cheat engine + [20.03.2024] - Add CHANGELOG.md - Set device in nestang_nano20k.gprj diff --git a/doc/cheats.md b/doc/cheats.md new file mode 100644 index 0000000..95dbf64 --- /dev/null +++ b/doc/cheats.md @@ -0,0 +1,35 @@ +# Cheat Wizard + +## Format + +This new module accepts `*.cwz` binary files with the following format: + +- 128 bits (16 bytes) per cheat + - Four bytes correspond to compare enabled/disabled + - Next 4 bytes correspond to address + - Next 4 bytes correspond to compare value + - Next 4 bytes correspond to replace value + +Example for `Battletoads.nes` + +``` +00000001 00000320 00000000 00000004 +00000001 000023a2 000000d6 00000024 +00000001 000026b5 00000001 00000000 +00000001 00004fba 00000008 0000002f +... +``` + +You'll need to use a hex editor to create the file. + +## How to use + +# Enable cheats + +Navigate to `2)Options->Cheats->Cheats Enabled:` and press `A` to enable or disable. + +## Load a cheat file + +Navigate to `2)Options->Cheats->Load cheat file` and press `A` to open the file explorer view. Choose the `.cwz` file for your game and press `A`, you'll see the message `Cheats loaded!` if the file was correctly loaded. + +After enabling cheats and loading them, you can load a `ROM` and cheats will be applied. \ No newline at end of file diff --git a/doc/images/wishbone_b4_piplined_singleAccess.JPG b/doc/images/wishbone_b4_piplined_singleAccess.JPG new file mode 100644 index 0000000..99352df Binary files /dev/null and b/doc/images/wishbone_b4_piplined_singleAccess.JPG differ diff --git a/doc/images/wishbone_b4_piplined_singleReadCycle.JPG b/doc/images/wishbone_b4_piplined_singleReadCycle.JPG new file mode 100644 index 0000000..d53cd7c Binary files /dev/null and b/doc/images/wishbone_b4_piplined_singleReadCycle.JPG differ diff --git a/doc/images/wishbone_b4_piplined_singleWriteCycle.JPG b/doc/images/wishbone_b4_piplined_singleWriteCycle.JPG new file mode 100644 index 0000000..1e33ec2 Binary files /dev/null and b/doc/images/wishbone_b4_piplined_singleWriteCycle.JPG differ diff --git a/doc/wishbone.md b/doc/wishbone.md new file mode 100644 index 0000000..261796a --- /dev/null +++ b/doc/wishbone.md @@ -0,0 +1,30 @@ +# Pipelined Wishbone B4 Bus + +## About + +- `iosys` is wishbone master +- No `FIFO` implemented +- No arbiter implemented +- 128bit wide bus for `cheat_wizard` + +- Slaves + - `wishbone_slaves.sh` + +## Read + + + +## Write + + + +## References + +- [Wishbone bus](https://en.wikipedia.org/wiki/Wishbone_(computer_bus)) +- [Wishbone B4 Specifications](https://cdn.opencores.org/downloads/wbspec_b4.pdf) +- [zipcpu](https://zipcpu.com/) +- [Building a simple bus](https://zipcpu.com/zipcpu/2017/05/23/simplebus.html) +- [Building a Simple Wishbone Master](https://zipcpu.com/blog/2017/06/08/simple-wb-master.html) +- [Building a Simple Wishbone Slave](https://zipcpu.com/zipcpu/2017/05/29/simple-wishbone.html) +- [Building Formal Assumptions to Describe Wishbone Behaviour](https://zipcpu.com/zipcpu/2017/11/07/wb-formal.html) +- [Building a very simple wishbone interconnect](https://zipcpu.com/blog/2017/06/22/simple-wb-interconnect.html) \ No newline at end of file diff --git a/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/README.txt b/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/README.txt new file mode 100644 index 0000000..519dea1 --- /dev/null +++ b/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/README.txt @@ -0,0 +1,9 @@ +NESTang - an FPGA NES implemented with Tang Nano 20K, Primer 25K + +What's new in v0.11: + +- Add a menu item to switch between snestang and nestang cores. Just put the .bin core files in /cores directory. + +See installation.pdf for instructions, including how to wire the SNES/NES controllers. + +Visit https://github.com/nand2mario/nestang for more instructions. \ No newline at end of file diff --git a/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/firmware.bin b/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/firmware.bin new file mode 100644 index 0000000..ff252ff Binary files /dev/null and b/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/firmware.bin differ diff --git a/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/installation.pdf b/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/installation.pdf new file mode 100644 index 0000000..b253bee Binary files /dev/null and b/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/installation.pdf differ diff --git a/nestang_nano20k.gprj b/nestang_nano20k.gprj index efec0df..016bb09 100644 --- a/nestang_nano20k.gprj +++ b/nestang_nano20k.gprj @@ -9,6 +9,7 @@ + @@ -43,6 +44,7 @@ + @@ -61,6 +63,7 @@ + diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..062a63d --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,9 @@ +wishbone_slave/ +wishbone_slave_bound/ +wishbone_slave_prf/ +wishbone_slave_cvr/ + +cheat_wizard/ +cheat_wizard_bound/ +cheat_wizard_prf/ +cheat_wizard_cvr/ \ No newline at end of file diff --git a/src/apu.v b/src/apu.v index 6c699c0..970983f 100644 --- a/src/apu.v +++ b/src/apu.v @@ -372,6 +372,226 @@ module TriangleChan ( endmodule +module TriangleChan_enhanced_5b ( + input logic clk, + input logic phi1, + input logic aclk1, + input logic aclk1_d, + input logic reset, + input logic cold_reset, + input logic allow_us, + input logic [1:0] Addr, + input logic [7:0] DIN, + input logic write, + input logic [7:0] lc_load, + input logic LenCtr_Clock, + input logic LinCtr_Clock, + input logic Enabled, + output logic [4:0] Sample, // Change output Sample to 5 bits + output logic IsNonZero +); + logic [10:0] Period, applied_period, TimerCtr; + logic [4:0] SeqPos; // Change SeqPos to 5 bits + logic [6:0] LinCtrPeriod, LinCtrPeriod_1, LinCtr; + logic LinCtrl, line_reload; + logic LinCtrZero; + logic lc; + + logic LenCtrZero; + logic subunit_write; + logic [4:0] sample_latch; // Change sample_latch to 5 bits + + assign LinCtrZero = ~|LinCtr; + assign IsNonZero = lc; + assign subunit_write = (Addr == 0 || Addr == 3) & write; + + // Adjust Sample assignment for 5-bit output + assign Sample = (applied_period > 1 || allow_us) ? (SeqPos[4:0] ^ {5{~SeqPos[4]}}) : sample_latch; + + LenCounterUnit LenTri ( + .clk (clk), + .reset (reset), + .cold_reset (cold_reset), + .aclk1 (aclk1), + .aclk1_d (aclk1_d), + .len_clk (LenCtr_Clock), + .load_value (lc_load), + .halt_in (DIN[7]), + .addr (Addr[0]), + .is_triangle (1'b1), + .write (subunit_write), + .enabled (Enabled), + .lc_on (lc) + ); + + always_ff @(posedge clk) begin + if (phi1) begin + if (TimerCtr == 0) begin + TimerCtr <= Period; + applied_period <= Period; + if (IsNonZero & ~LinCtrZero) + SeqPos <= SeqPos + 1'd1; + end else begin + TimerCtr <= TimerCtr - 1'd1; + end + end + + if (aclk1) begin + LinCtrPeriod_1 <= LinCtrPeriod; + end + + if (LinCtr_Clock) begin + if (line_reload) + LinCtr <= LinCtrPeriod_1; + else if (!LinCtrZero) + LinCtr <= LinCtr - 1'd1; + + if (!LinCtrl) + line_reload <= 0; + end + + if (write) begin + case (Addr) + 0: begin + LinCtrl <= DIN[7]; + LinCtrPeriod <= DIN[6:0]; + end + 2: begin + Period[7:0] <= DIN; + end + 3: begin + Period[10:8] <= DIN[2:0]; + line_reload <= 1; + end + endcase + end + + if (reset) begin + sample_latch <= 5'h1F; // Initialize to 5-bit value + Period <= 0; + TimerCtr <= 0; + SeqPos <= 0; + LinCtrPeriod <= 0; + LinCtr <= 0; + LinCtrl <= 0; + line_reload <= 0; + end + + if (applied_period > 1) sample_latch <= Sample; + end +endmodule + +module TriangleChan_enhanced_6b ( + input logic clk, + input logic phi1, + input logic aclk1, + input logic aclk1_d, + input logic reset, + input logic cold_reset, + input logic allow_us, + input logic [1:0] Addr, + input logic [7:0] DIN, + input logic write, + input logic [7:0] lc_load, + input logic LenCtr_Clock, + input logic LinCtr_Clock, + input logic Enabled, + output logic [5:0] Sample, + output logic IsNonZero +); + logic [10:0] Period, applied_period, TimerCtr; + logic [5:0] SeqPos; + logic [6:0] LinCtrPeriod, LinCtrPeriod_1, LinCtr; + logic LinCtrl, line_reload; + logic LinCtrZero; + logic lc; + + logic LenCtrZero; + logic subunit_write; + logic [5:0] sample_latch; + + assign LinCtrZero = ~|LinCtr; + assign IsNonZero = lc; + assign subunit_write = (Addr == 0 || Addr == 3) & write; + + assign Sample = (applied_period > 1 || allow_us) ? (SeqPos ^ {6{~SeqPos[5]}}) : sample_latch; + + LenCounterUnit LenTri ( + .clk (clk), + .reset (reset), + .cold_reset (cold_reset), + .aclk1 (aclk1), + .aclk1_d (aclk1_d), + .len_clk (LenCtr_Clock), + .load_value (lc_load), + .halt_in (DIN[7]), + .addr (Addr[0]), + .is_triangle (1'b1), + .write (subunit_write), + .enabled (Enabled), + .lc_on (lc) + ); + + always_ff @(posedge clk) begin + if (phi1) begin + if (TimerCtr == 0) begin + TimerCtr <= Period; + applied_period <= Period; + if (IsNonZero & ~LinCtrZero) + SeqPos <= SeqPos + 1'd1; + end else begin + TimerCtr <= TimerCtr - 1'd1; + end + end + + if (aclk1) begin + LinCtrPeriod_1 <= LinCtrPeriod; + end + + if (LinCtr_Clock) begin + if (line_reload) + LinCtr <= LinCtrPeriod_1; + else if (!LinCtrZero) + LinCtr <= LinCtr - 1'd1; + + if (!LinCtrl) + line_reload <= 0; + end + + if (write) begin + case (Addr) + 0: begin + LinCtrl <= DIN[7]; + LinCtrPeriod <= DIN[6:0]; + end + 2: begin + Period[7:0] <= DIN; + end + 3: begin + Period[10:8] <= DIN[2:0]; + line_reload <= 1; + end + endcase + end + + if (reset) begin + sample_latch <= 6'h3F; + Period <= 0; + TimerCtr <= 0; + SeqPos <= 0; + LinCtrPeriod <= 0; + LinCtr <= 0; + LinCtrl <= 0; + line_reload <= 0; + end + + if (applied_period > 1) sample_latch <= Sample; + end + +endmodule + + + module NoiseChan ( input logic clk, input logic ce, @@ -799,7 +1019,10 @@ module APU ( output logic [15:0] Sample, output logic DmaReq, // 1 when DMC wants DMA output logic [15:0] DmaAddr, // Address DMC wants to read - output logic IRQ // IRQ asserted high == asserted + output logic IRQ, // IRQ asserted high == asserted + // Enhanced APU + input logic apu_enhanced_ce, + input logic apu_mapper_saturates ); logic [7:0] len_counter_lut[32]; @@ -840,6 +1063,7 @@ module APU ( logic [4:0] Enabled; logic [3:0] Sq1Sample,Sq2Sample,TriSample,NoiSample; + logic [4:0] TriSample_enhanced; logic [6:0] DmcSample; logic DmcIrq; logic IsDmcActive; @@ -856,7 +1080,7 @@ module APU ( assign ApuMW4 = ADDR[4:2]>=4; // DMC assign ApuMW5 = ADDR[4:2]==5; // Control registers - logic Sq1NonZero, Sq2NonZero, TriNonZero, NoiNonZero; + logic Sq1NonZero, Sq2NonZero, TriNonZero, TriNonZero_enhanced, NoiNonZero; logic ClkE, ClkL; logic [4:0] enabled_buffer, enabled_buffer_1; @@ -884,8 +1108,7 @@ module APU ( assign ClkL = (frame_half & aclk1_delayed); // Generate bus output - assign DOUT = {DmcIrq, irq_flag, 1'b0, IsDmcActive, NoiNonZero, TriNonZero, - Sq2NonZero, Sq1NonZero}; + assign DOUT = {DmcIrq, irq_flag, 1'b0, IsDmcActive, NoiNonZero, TriNonZero, TriNonZero_enhanced, Sq2NonZero, Sq1NonZero}; assign IRQ = frame_irq || DmcIrq; @@ -953,6 +1176,25 @@ module APU ( .IsNonZero (TriNonZero) ); + TriangleChan_enhanced_5b Tri_enhanced ( + .clk (clk), + .phi1 (phi1), + .aclk1 (aclk1), + .aclk1_d (aclk1_delayed), + .reset (reset), + .cold_reset (cold_reset), + .allow_us (allow_us), + .Addr (ADDR[1:0]), + .DIN (DIN), + .write (ApuMW2 && write), + .lc_load (lc_load), + .LenCtr_Clock (ClkL), + .LinCtr_Clock (ClkE), + .Enabled (Enabled[2]), + .Sample (TriSample_enhanced), + .IsNonZero (TriNonZero_enhanced) + ); + NoiseChan Noi ( .clk (clk), .ce (ce), @@ -998,7 +1240,11 @@ module APU ( .noise (NoiSample), .triangle (TriSample), .dmc (DmcSample), - .sample (Sample) + .sample (Sample), + // Enhanced APU + .apu_enhanced_ce(apu_enhanced_ce), + .apu_triangle_enhanced(TriSample_enhanced), + .apu_mapper_saturates(apu_mapper_saturates) ); FrameCtr frame_counter ( @@ -1034,7 +1280,11 @@ module APUMixer ( input logic [3:0] triangle, input logic [3:0] noise, input logic [6:0] dmc, - output logic [15:0] sample + output logic [15:0] sample, + // Enhanced APU + input logic apu_enhanced_ce, + input logic [5:0] apu_triangle_enhanced, + input logic apu_mapper_saturates ); logic [15:0] pulse_lut[32]; @@ -1051,6 +1301,15 @@ assign tri_lut = '{ 6'h20, 6'h24, 6'h28, 6'h2C, 6'h30, 6'h34, 6'h38, 6'h3C }; +// Enhanced APU +logic [5:0] tri_lut_enhanced_5b[32]; +assign tri_lut_enhanced_5b = '{ + 6'h00, 6'h01, 6'h02, 6'h04, 6'h06, 6'h08, 6'h0A, 6'h0C, + 6'h0E, 6'h10, 6'h12, 6'h14, 6'h16, 6'h18, 6'h1A, 6'h1C, + 6'h1E, 6'h20, 6'h22, 6'h24, 6'h26, 6'h28, 6'h2A, 6'h2C, + 6'h2E, 6'h30, 6'h32, 6'h34, 6'h36, 6'h38, 6'h3A, 6'h3C +}; + logic [5:0] noise_lut[16]; assign noise_lut = '{ 6'h00, 6'h03, 6'h05, 6'h08, 6'h0B, 6'h0D, 6'h10, 6'h13, @@ -1145,11 +1404,94 @@ assign mix_lut = '{ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000 }; +logic [15:0] mix_lut_enhanced[512]; +assign mix_lut_enhanced = '{ + 16'h0000, 16'h0128, 16'h024F, 16'h0374, 16'h0497, 16'h05B8, 16'h06D7, 16'h07F5, + 16'h0911, 16'h0A2B, 16'h0B44, 16'h0C5B, 16'h0D71, 16'h0E84, 16'h0F96, 16'h10A7, + 16'h11B6, 16'h12C3, 16'h13CF, 16'h14DA, 16'h15E2, 16'h16EA, 16'h17EF, 16'h18F4, + 16'h19F6, 16'h1AF8, 16'h1BF7, 16'h1CF6, 16'h1DF3, 16'h1EEE, 16'h1FE9, 16'h20E1, + 16'h21D9, 16'h22CF, 16'h23C3, 16'h24B7, 16'h25A9, 16'h2699, 16'h2788, 16'h2876, + 16'h2963, 16'h2A4F, 16'h2B39, 16'h2C22, 16'h2D09, 16'h2DF0, 16'h2ED5, 16'h2FB9, + 16'h309B, 16'h317D, 16'h325D, 16'h333C, 16'h341A, 16'h34F7, 16'h35D3, 16'h36AD, + 16'h3787, 16'h385F, 16'h3936, 16'h3A0C, 16'h3AE1, 16'h3BB5, 16'h3C87, 16'h3D59, + 16'h3E29, 16'h3EF9, 16'h3FC7, 16'h4095, 16'h4161, 16'h422C, 16'h42F7, 16'h43C0, + 16'h4488, 16'h4550, 16'h4616, 16'h46DB, 16'h47A0, 16'h4863, 16'h4925, 16'h49E7, + 16'h4AA7, 16'h4B67, 16'h4C25, 16'h4CE3, 16'h4DA0, 16'h4E5C, 16'h4F17, 16'h4FD1, + 16'h508A, 16'h5142, 16'h51F9, 16'h52B0, 16'h5365, 16'h541A, 16'h54CE, 16'h5581, + 16'h5633, 16'h56E5, 16'h5795, 16'h5845, 16'h58F4, 16'h59A2, 16'h5A4F, 16'h5AFC, + 16'h5BA7, 16'h5C52, 16'h5CFC, 16'h5DA5, 16'h5E4E, 16'h5EF6, 16'h5F9D, 16'h6043, + 16'h60E8, 16'h618D, 16'h6231, 16'h62D4, 16'h6377, 16'h6418, 16'h64B9, 16'h655A, + 16'h65F9, 16'h6698, 16'h6736, 16'h67D4, 16'h6871, 16'h690D, 16'h69A8, 16'h6A43, + 16'h6ADD, 16'h6B76, 16'h6C0F, 16'h6CA7, 16'h6D3E, 16'h6DD5, 16'h6E6B, 16'h6F00, + 16'h6F95, 16'h7029, 16'h70BD, 16'h7150, 16'h71E2, 16'h7273, 16'h7304, 16'h7395, + 16'h7424, 16'h74B4, 16'h7542, 16'h75D0, 16'h765D, 16'h76EA, 16'h7776, 16'h7802, + 16'h788D, 16'h7917, 16'h79A1, 16'h7A2A, 16'h7AB3, 16'h7B3B, 16'h7BC3, 16'h7C4A, + 16'h7CD0, 16'h7D56, 16'h7DDB, 16'h7E60, 16'h7EE4, 16'h7F68, 16'h7FEB, 16'h806E, + 16'h80F0, 16'h8172, 16'h81F3, 16'h8274, 16'h82F4, 16'h8373, 16'h83F2, 16'h8471, + 16'h84EF, 16'h856C, 16'h85E9, 16'h8666, 16'h86E2, 16'h875E, 16'h87D9, 16'h8853, + 16'h88CD, 16'h8947, 16'h89C0, 16'h8A39, 16'h8AB1, 16'h8B29, 16'h8BA0, 16'h8C17, + 16'h8C8E, 16'h8D03, 16'h8D79, 16'h8DEE, 16'h8E63, 16'h8ED7, 16'h8F4A, 16'h8FBE, + 16'h9030, 16'h90A3, 16'h9115, 16'h9186, 16'h91F7, 16'h9268, 16'h92D8, 16'h9348, + 16'h93B8, 16'h9427, 16'h9495, 16'h9503, 16'h9571, 16'h95DF, 16'h964C, 16'h96B8, + 16'h9724, 16'h9790, 16'h97FB, 16'h9866, 16'h98D1, 16'h993B, 16'h99A5, 16'h9A0E, + 16'h9A77, 16'h9AE0, 16'h9B48, 16'h9BB0, 16'h9C18, 16'h9C7F, 16'h9CE6, 16'h9D4C, + 16'h9DB2, 16'h9E18, 16'h9E7D, 16'h9EE2, 16'h9F47, 16'h9FAB, 16'hA00F, 16'hA073, + 16'hA0D6, 16'hA139, 16'hA19B, 16'hA1FD, 16'hA25F, 16'hA2C1, 16'hA322, 16'hA383, + 16'hA3E3, 16'hA443, 16'hA4A3, 16'hA502, 16'hA562, 16'hA5C0, 16'hA61F, 16'hA67D, + 16'hA6DB, 16'hA738, 16'hA796, 16'hA7F2, 16'hA84F, 16'hA8AB, 16'hA907, 16'hA963, + 16'hA9BE, 16'hAA19, 16'hAA74, 16'hAACE, 16'hAB28, 16'hAB82, 16'hABDB, 16'hAC35, + 16'hAC8E, 16'hACE6, 16'hAD3E, 16'hAD96, 16'hADEE, 16'hAE46, 16'hAE9D, 16'hAEF4, + 16'hAF4A, 16'hAFA0, 16'hAFF6, 16'hB04C, 16'hB0A2, 16'hB0A2, 16'hB0A2, 16'hB0A2, + 16'hB0A2, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, + 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000 +}; + +// Square waves wire [4:0] squares = square1 + square2; wire [15:0] ch1 = pulse_lut[squares]; -wire [8:0] mix = 9'(tri_lut[triangle]) + 9'(noise_lut[noise]) + 9'(dmc_lut[dmc]); -wire [15:0] ch2 = mix_lut[mix]; -assign sample = ch1 + ch2; +// Normal mixer +wire [8:0] mix_normal = 9'(tri_lut[triangle]) + 9'(noise_lut[noise]) + 9'(dmc_lut[dmc]); +wire [15:0] ch2 = mix_lut[mix_normal]; +wire [15:0] sample_normal = ch1 + ch2; + +// Linear mixer + enhanced triangle wave +wire [8:0] mix_enhanced = 9'((tri_lut_enhanced_5b[apu_triangle_enhanced])) + 9'(noise_lut[noise]) + 9'(dmc_lut[dmc]); +wire [15:0] ch2_enhanced = mix_lut_enhanced[mix_enhanced >> 1]; +wire [15:0] sample_linear = ch1 + ch2_enhanced << 1; + +// Normal mixer + enhanced triangle wave +wire [15:0] ch2_mix_normal_enhanced_tri = mix_lut[mix_enhanced]; +wire [15:0] sample_mix_normal_enhanced_tri = ch1 + ch2_enhanced; + +assign sample = !apu_enhanced_ce ? sample_normal : + !apu_mapper_saturates ? sample_linear : + sample_mix_normal_enhanced_tri; endmodule diff --git a/src/cart.sv b/src/cart.sv index 9fcda71..a7526bd 100644 --- a/src/cart.sv +++ b/src/cart.sv @@ -53,7 +53,9 @@ module cart_top ( output reg [1:0] diskside_auto, input [1:0] diskside, input fds_busy, // FDS Disk Swap Busy - input fds_eject // FDS Disk Swap Pause + input fds_eject, // FDS Disk Swap Pause + // Enhanced APU + input i_enhanced_apu_ce ); tri0 prg_allow_b, vram_a10_b, vram_ce_b, chr_allow_b, irq_b; @@ -1302,29 +1304,29 @@ NesEvent nesev( // Notes : External audio needs to be mixed correctly. // // Games : Akamajou Densetsu, Esper Dream 2, Mouryou Senki Madara // //*****************************************************************************// -// VRC6 vrc6( -// .clk (clk), -// .ce (ce), -// .enable (me[24] | me[26]), -// .flags (flags), -// .prg_ain (prg_ain), -// .prg_aout_b (prg_addr_b), -// .prg_read (prg_read), -// .prg_write (prg_write), -// .prg_din (prg_din), -// .prg_dout_b (prg_dout_b), -// .prg_allow_b(prg_allow_b), -// .chr_ain (chr_ain), -// .chr_aout_b (chr_addr_b), -// .chr_read (chr_read), -// .chr_allow_b(chr_allow_b), -// .vram_a10_b (vram_a10_b), -// .vram_ce_b (vram_ce_b), -// .irq_b (irq_b), -// .flags_out_b(flags_out_b), -// .audio_in (vrc6_audio), -// .audio_b (audio_out_b) -// ); +VRC6 vrc6( + .clk (clk), + .ce (ce), + .enable (me[24] | me[26]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .prg_read (prg_read), + .prg_write (prg_write), + .prg_din (prg_din), + .prg_dout_b (prg_dout_b), + .prg_allow_b(prg_allow_b), + .chr_ain (chr_ain), + .chr_aout_b (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (vrc6_audio), + .audio_b (audio_out_b) +); //*****************************************************************************// // Name : Konami VRC-7 // @@ -1828,17 +1830,18 @@ wire [15:0] vrc7_audio; // ); wire [15:0] vrc6_audio; -// vrc6_mixed snd_vrc6 ( -// .clk(clk), -// .ce(ce), -// .enable(me[24] | me[26] | (me[31] && exp_audioe[0])), -// .wren(prg_write), -// .addr_invert(me[26]), -// .addr_in(prg_ain), -// .data_in(prg_din), -// .audio_in(audio_in), -// .audio_out(vrc6_audio) -// ); +vrc6_mixed snd_vrc6 ( + .clk(clk), + .ce(ce), + .enable(me[24] | me[26] | (me[31] && exp_audioe[0])), + .wren(prg_write), + .addr_invert(me[26]), + .addr_in(prg_ain), + .data_in(prg_din), + .audio_in(audio_in), + .audio_out(vrc6_audio), + .i_enhanced_apu_ce(i_enhanced_apu_ce) +); reg [6:0] prg_mask; diff --git a/src/cheat_wizard.sby b/src/cheat_wizard.sby new file mode 100644 index 0000000..5ccdc3d --- /dev/null +++ b/src/cheat_wizard.sby @@ -0,0 +1,27 @@ +[tasks] +bound +prf +cvr + +[options] +bound: mode bmc +bound: depth 128 +prf: mode prove +prf: depth 64 +cvr: mode cover +cvr: depth 64 +cvr: append 20 + + +[engines] +bound: smtbmc +prf: smtbmc yices +cvr: smtbmc + +[script] +read_verilog -formal cheat_wizard.v +prep -top cheat_wizard + +[files] +cheat_wizard.v +wishbone_slaves.vh diff --git a/src/cheat_wizard.v b/src/cheat_wizard.v new file mode 100644 index 0000000..0247d3e --- /dev/null +++ b/src/cheat_wizard.v @@ -0,0 +1,199 @@ +module cheat_wizard( + input wire i_clk, + input wire i_reset_n, + // Cheats + input wire i_cheats_enabled, + input wire i_cheats_loaded, + input wire [23:0] i_sram_address, + input wire [7:0] i_sram_data, + output wire o_cheat_stb, + output wire [7:0] o_sram_data, + // Wishbone slave + input wire i_wb_cyc, + input wire i_wb_stb, + input wire i_wb_we, + input wire i_wb_err, + input wire [1:0] i_wb_addr, + input wire [128:0] i_wb_idata, + output wire o_wb_ack, + output wire o_wb_stall, + output wire o_wb_err + ); +// Wishbone Cheat Data FSM +reg [7:0] cheats_nr; +reg [7:0] cheats_nr_last; +reg cheats_compare_enabled_temp; +reg [16:0] cheats_address_temp; +reg [7:0] cheat_compare_value_temp; +reg [7:0] cheats_replace_value_temp; +reg cheats_compare_enabled[3:0]; +reg [16:0] cheats_address[3:0]; +reg [7:0] cheat_compare_value[3:0]; +reg [7:0] cheats_replace_value[3:0]; +reg [1:0] cheat_data_ix; + +// Cheats stuff +wire cheat_compare_stb_0; +wire cheat_compare_stb_1; +wire cheat_compare_stb_2; +wire cheat_compare_stb_3; +wire cheats_nr_stb; + +assign cheats_nr_stb = (cheats_nr_last != cheats_nr); + +assign cheat_compare_stb_0 = (~cheats_compare_enabled[0] ? 1'b1 : (i_sram_data == cheat_compare_value[0])); +assign cheat_compare_stb_1 = (~cheats_compare_enabled[1] ? 1'b1 : (i_sram_data == cheat_compare_value[1])); +assign cheat_compare_stb_2 = (~cheats_compare_enabled[2] ? 1'b1 : (i_sram_data == cheat_compare_value[2])); +assign cheat_compare_stb_3 = (~cheats_compare_enabled[3] ? 1'b1 : (i_sram_data == cheat_compare_value[3])); +assign o_cheat_stb = ( (i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == cheats_address[0])&&(cheat_compare_stb_0) ) || + ( (i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == cheats_address[1])&&(cheat_compare_stb_1) ) || + ( (i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == cheats_address[2])&&(cheat_compare_stb_2) ) || + ( (i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == cheats_address[3])&&(cheat_compare_stb_3) ); +assign o_sram_data = !o_cheat_stb ? i_sram_data : + cheat_compare_stb_0 ? cheats_replace_value[0] : + cheat_compare_stb_1 ? cheats_replace_value[1] : + cheat_compare_stb_2 ? cheats_replace_value[2] : + cheat_compare_stb_3 ? cheats_replace_value[3] : + 8'h00; + + +// Wishbone stuff +reg wb_ack; +reg wb_err; +reg [128:0] wb_odata; +reg wb_stall; + +initial wb_ack = 0; +initial wb_err = 0; +initial wb_odata = 0; +initial wb_stall= 0; + +always @(posedge i_clk) begin + if((~i_reset_n)||(i_wb_err)) begin + end else begin + // cheats_data + if(i_reset_n) begin + if((i_wb_stb)&&(i_wb_we)&&(i_wb_we)&&((i_wb_addr == 2'h01))&&(i_wb_we)&&(~o_wb_err)) begin + wb_odata <= i_wb_idata; + wb_stall <= 1'b1; + cheat_data_ix <= 2'b00; + + cheats_compare_enabled[i_wb_idata[111:104] - 1'b1] <= i_wb_idata[96]; + cheats_address[i_wb_idata[111:104] - 1'b1] <= i_wb_idata[79:64]; + cheat_compare_value[i_wb_idata[111:104] - 1'b1] <= i_wb_idata[39:32]; + cheats_replace_value[i_wb_idata[111:104] - 1'b1] <= i_wb_idata[7:0]; + + end else + wb_stall <= 1'b0; + end + end +end + +assign o_wb_ack = i_wb_stb; +assign o_wb_stall = wb_stall; +assign o_wb_err = wb_err; + +// +// Formal methods +// +`ifdef FORMAL + // f_past_valid + reg f_past_valid; + initial f_past_valid = 1'b0; + initial assert(!f_past_valid); + always @(posedge i_clk) + f_past_valid = 1'b1; + + // BMC Assumptions + always @(posedge i_clk) + if((f_past_valid)&&((~$past(i_reset_n))||(~$past(i_wb_err)))) begin + assume((i_wb_addr == 2'h01)||(i_wb_addr == 2'h02)||(i_wb_addr == 2'h03)); + assume($stable(i_wb_addr)); + assume($stable(i_wb_idata)); + end + + always @(posedge i_clk) + if((f_past_valid)&&((~$past(i_reset_n))||(~$past(i_wb_err)))) + if(i_wb_addr == 2'h01) + assume(i_wb_idata == 128'h00000000_00000000_00000000_00000001); + + always @(posedge i_clk) + if((f_past_valid)&&((~$past(i_reset_n))||(~$past(i_wb_err)))) + if(i_wb_addr == 2'h02) + assume(i_wb_idata == 128'h00000000_00000000_00000000_00000001); + + always @(posedge i_clk) + if((f_past_valid)&&((~$past(i_reset_n))||(~$past(i_wb_err)))) + if(i_wb_addr == 2'h03) + assume(i_wb_idata == 128'h00000001_000023A2_000000D6_00000024); + + always @(posedge i_clk) + if((f_past_valid)&&(~$past(f_past_valid))) begin + assume(i_wb_stb); + assume(i_wb_we); + assume(~i_wb_err); + assume(~i_wb_cyc); + end else begin + assume(~i_wb_stb); + assume(~i_wb_we); + assume(~i_wb_err); + assume(i_wb_cyc); + end + + always @(posedge i_clk) + if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n)))) + if($past(i_cheats_enabled)) + assume(i_cheats_enabled == $past(i_cheats_enabled)); + always @(posedge i_clk) + if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n)))) + if($past(i_cheats_loaded)) + assume(i_cheats_loaded == $past(i_cheats_loaded)); + + + // BMC Assertions + always @(posedge i_clk) + if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n))||(~$past(i_wb_err)))) begin + assert(wb_err == 1'b0); + end + + // Cover + always @(posedge i_clk) + if((f_past_valid)&&(i_reset_n)) + if((i_wb_stb)&&(i_wb_we)&&(~i_wb_err)&&(~o_wb_stall)&&(i_wb_addr == 2'h03)&&(i_wb_idata == 128'h00000001_000023A2_000000D6_00000024)) + if((i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == 23'h00_23A2)&&(i_sram_data == 8'hD6)) + cover(o_cheat_stb); + + // Contract + always @(posedge i_clk) + if((f_past_valid)&&($past(i_reset_n))&&(i_reset_n)&&(($past(i_wb_stb))&&($past(i_wb_cyc))&&($past(i_wb_we))&&(~$past(i_wb_err))&&($past(i_wb_addr) == 1))) begin + assert(wb_odata == i_wb_idata); + end + + always @(posedge i_clk) + if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n))||(~$past(i_wb_err)))) + if(i_wb_stb) + assert(o_wb_ack); + + always @(posedge i_clk) + if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n))||(~$past(i_wb_err)))) + assert(cheat_compare_stb_0 == (~cheats_compare_enabled[0] ? 1'b1 : (i_sram_data == cheat_compare_value[0]))); + + always @(posedge i_clk) + if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n))||(~$past(i_wb_err)))) + if((cheat_compare_stb_0)&&(~cheat_compare_stb_1)&&(~cheat_compare_stb_2)&&(~cheat_compare_stb_3)) + assert(o_cheat_stb == ( (i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == cheats_address[0])&&(cheat_compare_stb_0) )); + + always @(posedge i_clk) + if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n))||(~$past(i_wb_err)))) + if((i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == cheats_address[0])&&(i_sram_data == cheat_compare_value[0])) + if((cheat_compare_stb_0)&&(~cheat_compare_stb_1)&&(~cheat_compare_stb_2)&&(~cheat_compare_stb_3)) begin + assert(o_sram_data == cheats_replace_value[0]); + end + + // Induction assumptions + + // Induction assertions + +`endif // FORMAL + +endmodule \ No newline at end of file diff --git a/src/game_loader.v b/src/game_loader.v index cf16bf9..94d1b85 100644 --- a/src/game_loader.v +++ b/src/game_loader.v @@ -21,7 +21,9 @@ module GameLoader output reg busy, output reg done, output reg error, - output reg rom_loaded + output reg rom_loaded, + // APU Enhancement + output [7:0] o_mapper ); reg [7:0] prgsize; @@ -72,6 +74,8 @@ wire [3:0] prg_nvram = (is_nes20 ? ines[10][7:4] : 4'h0); wire piano = is_nes20 && (ines[15][5:0] == 6'h19); wire has_saves = ines[6][1]; +assign o_mapper = mapper; + assign mapper_flags[63:35] = 'd0; assign mapper_flags[34:31] = prg_nvram; //NES 2.0 Save RAM shift size (64 << size) assign mapper_flags[30] = piano; diff --git a/src/iosys/iosys.v b/src/iosys/iosys.v index 5888e56..25ca94f 100644 --- a/src/iosys/iosys.v +++ b/src/iosys/iosys.v @@ -31,6 +31,7 @@ `define PICOSOC_V module iosys #( + `include "wishbone_slaves.vh", parameter FREQ=21_477_000, parameter [14:0] COLOR_LOGO=15'b00000_10101_00000, parameter [15:0] CORE_ID=1 // 1: nestang, 2: snestang @@ -83,7 +84,35 @@ module iosys #( input sd_dat0, // MISO output sd_dat1, // 1 output sd_dat2, // 1 - output sd_dat3 // 0 for SPI mode + output sd_dat3, // 0 for SPI mode + + // Enhanced APU + output o_reg_enhanced_apu, + + // Wishbone master + // The return bus wires + input wire i_wb_ack, + input wire i_wb_stall, + input wire [31:0] i_wb_idata, + input wire i_wb_err, + // The bus control output wires + output wire o_wb_cyc, + output wire o_wb_stb, + output wire o_wb_we, + input wire o_wb_err, + output wire [1:0] o_wb_addr, + output wire [128:0] o_wb_odata, + output wire [3:0] o_wb_sel, + + // Cheats + output wire o_cheats_enabled, + output wire o_cheats_loaded, + // Debug + output wire [1:0] o_dbg_led, + // System Type + output wire [1:0] o_sys_type, + // Aspect Ratio + output wire o_reg_aspect_ratio ); /* verilator lint_off PINMISSING */ @@ -106,6 +135,12 @@ reg flash_wr; wire [31:0] spiflash_reg_do; wire spiflash_reg_wait; +// BSRAM - Use internal BRAM +localparam NES_BSRAM_SIZE = 32'h2000; +reg [7:0] reg_save_bsram; +reg [7:0] reg_load_bsram; +reg [7:0] reg_bsram [NES_BSRAM_SIZE-1:0]; + always @(posedge clk) begin if (~resetn) begin flash_loaded <= 0; @@ -179,12 +214,47 @@ wire cycle_reg_sel = mem_valid && (mem_addr == 32'h0200_0054); // c wire id_reg_sel = mem_valid && (mem_addr == 32'h0200_0060); +wire id_reg_enhanced_apu_sel = mem_valid && (mem_addr == 32'h0200_0080); + wire spiflash_reg_byte_sel = mem_valid && (mem_addr == 32'h0200_0070); wire spiflash_reg_word_sel = mem_valid && (mem_addr == 32'h0200_0074); wire spiflash_reg_ctrl_sel = mem_valid && (mem_addr == 32'h0200_0078); +wire id_reg_enhanced_apu_sel = mem_valid && (mem_addr == 32'h0200_0080); + +// Cheats +wire reg_cheats_enabled_sel = mem_valid && (mem_addr == 32'h0200_00A0); +wire reg_cheats_loaded_sel = mem_valid && (mem_addr == 32'h0200_00C0); +wire id_reg_cheats_sel_3 = mem_valid && (mem_addr == 32'h0200_00E0); // MMSB +wire id_reg_cheats_sel_2 = mem_valid && (mem_addr == 32'h0200_0100); +wire id_reg_cheats_sel_1 = mem_valid && (mem_addr == 32'h0200_0120); +wire id_reg_cheats_sel_0 = mem_valid && (mem_addr == 32'h0200_0140); // LLSB +wire id_reg_cheats_data_ready_sel = mem_valid && (mem_addr == 32'h0200_0160); + +// BSRAM +wire id_reg_save_bsram = mem_valid && (mem_addr == 32'h0200_0180); +wire id_reg_load_bsram = mem_valid && (mem_addr == 32'h0200_01A0); + +// System Type +wire id_reg_sys_type = mem_valid && (mem_addr == 32'h0200_01C0); + +// Aspect Ratio +wire id_reg_aspect_ratio = mem_valid && (mem_addr == 32'h0200_01E0); + +// Timer Interrupts +wire id_reg_timer_interrupts = mem_valid && (mem_addr == 32'h0200_0200); +// Timer0 Load Register +wire id_reg_timer0_load_value = mem_valid && (mem_addr == 32'h0200_0220); assign mem_ready = ram_ready || textdisp_reg_char_sel || simpleuart_reg_div_sel || - romload_reg_ctrl_sel || romload_reg_data_sel || joystick_reg_sel || time_reg_sel || cycle_reg_sel || id_reg_sel || + romload_reg_ctrl_sel || romload_reg_data_sel || joystick_reg_sel || time_reg_sel || id_reg_sel || cycle_reg_sel || id_reg_sel || + id_reg_enhanced_apu_sel || + reg_cheats_enabled_sel || reg_cheats_loaded_sel || id_reg_cheats_data_ready_sel || + id_reg_save_bsram || id_reg_load_bsram || + id_reg_sys_type || + id_reg_aspect_ratio || + id_reg_timer_interrupts || + id_reg_timer0_load_value || + id_reg_cheats_sel_0 || id_reg_cheats_sel_1 || id_reg_cheats_sel_2 || id_reg_cheats_sel_3 || (simpleuart_reg_dat_sel && !simpleuart_reg_dat_wait) || ((simplespimaster_reg_byte_sel || simplespimaster_reg_word_sel) && !simplespimaster_reg_wait) || (spiflash_reg_byte_sel || spiflash_reg_word_sel) && !spiflash_reg_wait || @@ -197,6 +267,20 @@ assign mem_rdata = ram_ready ? ram_rdata : time_reg_sel ? time_reg : cycle_reg_sel ? cycle_reg : id_reg_sel ? {16'b0, CORE_ID} : + id_reg_enhanced_apu_sel ? reg_enhanced_apu : + reg_cheats_enabled_sel ? {31'h0, reg_cheats_enabled} : + reg_cheats_loaded_sel ? {31'h0, reg_cheats_loaded} : + id_reg_cheats_data_ready_sel ? {31'h0, reg_cheats_data_ready} : + id_reg_save_bsram ? {31'h0, reg_save_bsram} : + id_reg_load_bsram ? {31'h0, reg_load_bsram} : + id_reg_sys_type ? {30'b00_0000_0000_0000, reg_sys_type} : + id_reg_aspect_ratio ? {31'b000_0000_0000_0000, reg_aspect_ratio} : + id_reg_timer_interrupts ? {reg_timer_interrupts} : + id_reg_timer0_load_value ? {reg_timer0_load_value} : + id_reg_cheats_sel_3 ? reg_cheats[128:96] : + id_reg_cheats_sel_2 ? reg_cheats[95:64] : + id_reg_cheats_sel_1 ? reg_cheats[63:32] : + id_reg_cheats_sel_0 ? reg_cheats[31:0] : (simplespimaster_reg_byte_sel | simplespimaster_reg_word_sel) ? simplespimaster_reg_do : (spiflash_reg_byte_sel | spiflash_reg_word_sel) ? spiflash_reg_do : 32'h 0000_0000; @@ -344,8 +428,213 @@ always @(posedge clk) begin end end +// Enhanced RAM register +reg reg_enhanced_apu; +always @(posedge clk) begin + if(~resetn) begin + reg_enhanced_apu <= 0; + end + if(mem_addr == 32'h0200_0080) begin + reg_enhanced_apu <= mem_wdata; + end +end + +assign o_reg_enhanced_apu = reg_enhanced_apu; + // assign led = ~{2'b0, (^ total_refresh[7:0]), s0, flash_cnt[12]}; // flash while loading +// Enhanced RAM register +reg reg_enhanced_apu; +always @(posedge clk) begin + if(~resetn) begin + reg_enhanced_apu <= 0; + end + if(mem_addr == 32'h0200_0080) begin + reg_enhanced_apu <= mem_wdata; + end +end + +assign o_reg_enhanced_apu = reg_enhanced_apu; + +// +// Cheat engine +// +reg reg_cheats_enabled; +reg reg_cheats_loaded_last; +reg reg_cheats_loaded; +reg reg_cheats_enabled_last; +reg reg_cheats_data_ready; +reg reg_cheats_data_ready_last; +reg [128:0] reg_cheats; +reg [128:0] reg_cheats_last; +reg reg_cheats_data_sent; + +wire reg_cheats_enabled_stb; +wire reg_cheats_loaded_stb; +wire reg_cheats_data_ready_stb; +wire reg_cheats_stb; + +initial reg_cheats_enabled = 1'b0; +initial reg_cheats_enabled_last = 1'b0; +initial reg_cheats_loaded = 1'b0; +initial reg_cheats_loaded_last = 1'b0; +initial reg_cheats_data_ready = 1'b0; +initial reg_cheats_data_ready_last = 1'b0; +initial reg_cheats = 0; +initial reg_cheats_last = 0; + +assign reg_cheats_enabled_stb = (reg_cheats_enabled_last != reg_cheats_enabled); +assign reg_cheats_loaded_stb = (reg_cheats_loaded_last != reg_cheats_loaded); +assign reg_cheats_data_ready_stb = (reg_cheats_data_ready_last != reg_cheats_data_ready); +assign reg_cheats_stb = (reg_cheats_last != reg_cheats); + +always @(posedge clk) begin + if(~resetn) + reg_cheats <= 0; + else begin + if(mem_addr == 32'h0200_00A0) + reg_cheats_enabled <= mem_wdata[0]; + if(mem_addr == 32'h0200_00C0) + reg_cheats_loaded <= mem_wdata[0]; + if(mem_addr == 32'h0200_00E0) + reg_cheats[127:96] <= mem_wdata; + if(mem_addr == 32'h0200_0100) + reg_cheats[95:64] <= mem_wdata; + if(mem_addr == 32'h0200_0120) + reg_cheats[63:32] <= mem_wdata; + if(mem_addr == 32'h0200_0140) + reg_cheats[31:0] <= mem_wdata; + if(mem_addr == 32'h0200_0160) + reg_cheats_data_ready <= mem_wdata[0]; + + + if(reg_cheats_enabled_stb) + reg_cheats_enabled_last <= reg_cheats_enabled; + if(reg_cheats_loaded_stb) + reg_cheats_loaded_last <= reg_cheats_loaded; + if(reg_cheats_stb) + reg_cheats_last <= reg_cheats; + if(reg_cheats_loaded_stb) + reg_cheats_data_ready_last <= reg_cheats_data_ready; + else if((reg_cheats_data_ready)&&(~wb_cyc)) + reg_cheats_data_ready <= 1'b0; + end +end + +assign o_cheats_enabled = reg_cheats_enabled; +assign o_cheats_loaded = reg_cheats_loaded; + +// +// Wishbone master +// +reg [1:0] wb_addr; +reg [128:0] wb_odata; +reg wb_we; +reg wb_cyc; +reg wb_stb; +reg wb_err; + +initial wb_err = 1'b0; +initial wb_cyc = 1'b0; +initial wb_stb = 1'b0; + + +always @(posedge clk) begin + if((~resetn)||(i_wb_err)) begin + wb_cyc <= 1'b0; + wb_stb <= 1'b0; + if(~resetn) begin + // ToDo: reset stuff + end + end else begin + // ToDo: Do bus stuff + // cheats_data + if((reg_cheats_enabled)&&(reg_cheats_data_ready)&&(~i_wb_stall)&&(~wb_cyc)) begin + // wb_addr <= WISHBONE_SLAVE_ADDRESS_CHEATS_DATA; + wb_addr <= 2'h01; + wb_odata <= reg_cheats; + wb_we <= 1'b1; + wb_cyc <= 1'b1; + wb_stb <= 1'b1; + end + if(wb_cyc) begin + wb_addr <= 0; + wb_we <= 1'b0; + wb_stb <= 1'b0; + if(i_wb_ack) + wb_cyc <= 1'b0; + end + end +end + +assign o_wb_addr = wb_addr; +assign o_wb_odata = wb_odata; +assign o_wb_we = wb_we; +assign o_wb_stb = wb_stb; +assign o_wb_cyc = wb_cyc; + +// System Type +reg [1:0] reg_sys_type; +initial reg_sys_type = 2'b00; // NTSC/Dendy +always @(posedge clk) begin + if(~resetn) + reg_sys_type <= 2'b00; + else begin + if(mem_addr == 32'h0200_01C0) + reg_sys_type <= mem_wdata[1:0]; + end +end + +assign o_sys_type = reg_sys_type; + +// Aspect Ratio +reg reg_aspect_ratio; +initial reg_aspect_ratio = 1'b0; // 1:1 +always @(posedge clk) begin + if(~resetn) + reg_aspect_ratio <= 1'b0; + else begin + if(mem_addr == 32'h0200_01E0) + reg_aspect_ratio <= mem_wdata[0]; + end +end + +assign o_reg_aspect_ratio = reg_aspect_ratio; + +// Timer Interrupts +reg [31:0] reg_timer_interrupts; +initial reg_timer_interrupts <= 32'h0; +always @(posedge clk) + if(~resetn) + reg_timer_interrupts <= 32'h0; + else begin + if(mem_addr == 32'h0200_0200) + reg_timer_interrupts <= mem_wdata[31:0]; + if(timer0_counter == reg_timer0_load_value) + reg_timer_interrupts <= reg_timer_interrupts & 32'h01; + end + +// Timer0 +reg [31:0] timer0_counter; +reg [31:0] reg_timer0_load_value; +initial timer0_counter = 32'h0; +initial reg_timer0_load_value = 32'h0; +// Write register +always @(posedge clk) + if(~resetn) + reg_timer0_load_value <= 32'h0; + else + if(mem_addr == 32'h0200_0220) + reg_timer0_load_value <= mem_wdata[31:0]; +// Counter +always @(posedge clk) + if(~resetn) + timer0_counter <= 32'h0; + else if(timer0_counter < reg_timer0_load_value) + timer0_counter <= timer0_counter + 1; + else + timer0_counter <= 32'h0; + endmodule module picosoc_regs ( diff --git a/src/mappers/VRC.sv b/src/mappers/VRC.sv index 64d987d..d9e9964 100644 --- a/src/mappers/VRC.sv +++ b/src/mappers/VRC.sv @@ -605,7 +605,9 @@ module MAPVRC6( //signal descriptions in powerpak.v input ce,// add //output [15:0] audio, - input mapper26 + input mapper26, + // Enhanced APU + input i_enhanced_apu_ce ); //wire [15:0] ain=prgain; //MAP18 @@ -842,7 +844,8 @@ module vrc6_mixed ( input [15:0] addr_in, input [7:0] data_in, input [15:0] audio_in, // Inverted audio from APU - output [15:0] audio_out + output [15:0] audio_out, + input i_enhanced_apu_ce ); vrc6sound snd_vrc6 ( @@ -855,7 +858,11 @@ vrc6sound snd_vrc6 ( .din(data_in), .outSq1(vrc6sq1_out), .outSq2(vrc6sq2_out), - .outSaw(vrc6saw_out) + .outSaw(vrc6saw_out), + // Enhanced APU + .outSq1_enhanced(vrc6sq1_out_enhanced), + .outSq2_enhanced(vrc6sq2_out_enhanced), + .outSaw_enhanced(vrc6saw_out_enhanced) ); //sound @@ -864,10 +871,15 @@ vrc6sound snd_vrc6 ( wire [3:0] vrc6sq1_out; wire [3:0] vrc6sq2_out; wire [4:0] vrc6saw_out; + // Enhanced APU + wire [3:0] vrc6sq1_out_enhanced; + wire [3:0] vrc6sq2_out_enhanced; + wire [7:0] vrc6saw_out_enhanced; // VRC6 sound is mixed before amplification, and them amplified linearly wire [5:0] exp_audio = vrc6sq1_out + vrc6sq2_out + vrc6saw_out; - wire [15:0] audio = {exp_audio, exp_audio, exp_audio[5:2]}; + wire [7:0] exp_audio_enhanced = vrc6sq1_out_enhanced + vrc6sq2_out_enhanced + vrc6saw_out_enhanced >> 2; + wire [15:0] audio = (i_enhanced_apu_ce ? {exp_audio_enhanced, exp_audio_enhanced, exp_audio_enhanced[5:2]} : {exp_audio, exp_audio, exp_audio[5:2]}); // VRC6 audio is much louder than APU audio, so match the levels we have to reduce it // to about 43% to match the audio ratio of the original Famicom with AD3. Note that the @@ -886,9 +898,12 @@ module vrc6sound( input addr_invert, input [15:0] addr_in, input [7:0] din, - output [3:0] outSq1, //range=0..0x0F - output [3:0] outSq2, //range=0..0x0F - output [4:0] outSaw //range=0..0x1F + output [3:0] outSq1, //range=0..0x0F + output [3:0] outSq2, //range=0..0x0F + output [4:0] outSaw, //range=0..0x1F + output [7:0] outSq1_enhanced, //range=0..0xFF + output [7:0] outSq2_enhanced, //range=0..0xFF + output [7:0] outSaw_enhanced //range=0..0xFF ); wire [15:0] ain=addr_invert ? {addr_in[15:2],addr_in[0],addr_in[1]} : addr_in; //MAP1A : MAP18 @@ -905,6 +920,10 @@ reg en0, en1, en2; reg [3:0] duty0cnt, duty1cnt; reg [2:0] duty2cnt; reg [7:0] acc; +reg [7:0] acc_enhanced; + +reg [7:0] sq1_acc_enhanced; +reg [7:0] sq2_acc_enhanced; always@(posedge clk) begin if(~enable) begin @@ -960,6 +979,34 @@ always@(posedge clk) begin end end +always @(posedge clk) begin + if (enable && ce) begin + // Calculate enhanced square waveforms + if (sq1_acc_enhanced == 0) begin + sq1_acc_enhanced <= 255; + end else begin + sq1_acc_enhanced <= sq1_acc_enhanced - 1; + end + + if (sq2_acc_enhanced == 0) begin + sq2_acc_enhanced <= 255; + end else begin + sq2_acc_enhanced <= sq2_acc_enhanced - 1; + end + end +end + +always @(posedge clk) begin + if (enable && ce) begin + // Calculate enhanced sawtooth waveform + if (acc_enhanced == 0) begin + acc_enhanced <= 255; + end else begin + acc_enhanced <= acc_enhanced - 1; + end + end +end + wire [4:0] duty0pos=duty0cnt+{1'b1,~duty0}; wire [4:0] duty1pos=duty1cnt+{1'b1,~duty1}; wire [3:0] ch0=((~duty0pos[4]|mode0)&en0)?vol0:4'd0; @@ -970,5 +1017,9 @@ assign outSq1=ch0; assign outSq2=ch1; assign outSaw=ch2; +assign outSq1_enhanced = sq1_acc_enhanced; +assign outSq2_enhanced = sq2_acc_enhanced; +assign outSaw_enhanced = acc_enhanced >> 2; + endmodule diff --git a/src/nes.v b/src/nes.v index 27a2d53..0f828e6 100644 --- a/src/nes.v +++ b/src/nes.v @@ -118,7 +118,10 @@ module NES( output gg_avail, input gg_reset, output [2:0] emphasis, - output save_written + output save_written, + // Enhanced APU + input i_APU_enhancements_ce, + input i_APU_mapper_saturates ); @@ -331,6 +334,7 @@ DmaController dma( wire apu_cs = addr >= 'h4000 && addr < 'h4018; wire [7:0] apu_dout; wire [15:0] sample_apu; +wire NES_APU_enhancements_ce; APU apu( .MMC5 (1'b0), @@ -353,7 +357,9 @@ APU apu( .DmaData (from_data_bus), .odd_or_even (odd_or_even), .IRQ (apu_irq), - .allow_us(1'b0) + .allow_us(1'b0), + .apu_enhanced_ce(i_APU_enhancements_ce), + .apu_mapper_saturates(i_APU_mapper_saturates) ); assign sample = sample_a; @@ -494,7 +500,9 @@ cart_top multi_mapper ( .fds_eject (fds_eject), // Used to trigger FDS disk changes .fds_busy (fds_busy), // Used to trigger FDS disk changes .diskside_auto (diskside_req), - .diskside (diskside) + .diskside (diskside), + // Enhanced APU + .i_enhanced_apu_ce(NES_APU_enhancements_ce) ); wire genie_ovr; diff --git a/src/nes2hdmi.sv b/src/nes2hdmi.sv index 2a2c4df..634a24e 100644 --- a/src/nes2hdmi.sv +++ b/src/nes2hdmi.sv @@ -12,7 +12,9 @@ module nes2hdmi ( input [8:0] cycle, input [8:0] scanline, input [15:0] sample, - input aspect_8x7, // 1: 8x7 pixel aspect ratio mode + + // Aspect Ratio + input wire i_reg_aspect_ratio, // overlay interface input overlay, @@ -73,9 +75,6 @@ localparam BAUDRATE=115200; `define EMBED_GAME `endif -// flags -logic asp8x7_on = 1'b1; - // video stuff wire [9:0] cy, frameHeight; wire [10:0] cx, frameWidth; @@ -212,7 +211,7 @@ end // - For 8:7, total width is 36*24 + 13 = 877. Therefore x goes from 201 to 1077. reg r2_active; reg [4:0] xs, r_xs, r2_xs; // x step for each 7 NES pixel group, 0-23 for 8:7 pixel aspect ratio, or 0-20 for 1:1 pixel aspect ratio -wire xload = asp8x7_on ? +wire xload = i_reg_aspect_ratio ? (xs == 5'd0 || xs == 5'd3 || xs == 5'd6 || xs == 5'd10 || xs == 5'd13 || xs == 5'd17 || xs == 5'd20) : (xs == 5'd0 || xs == 5'd3 || xs == 5'd6 || xs == 5'd9 || xs == 5'd12 || xs == 5'd15 || xs == 5'd18); reg r_xload; @@ -224,7 +223,7 @@ reg [23:0] NES_PALETTE [0:63]; // Mix ratio of border pixels for 8x7 pixel aspect ratio reg [15:0] mixratio; reg mix; -wire [15:0] next_mixratio = ~asp8x7_on ? 16'b0 : // no mixing for 1:1 pixel aspect ratio +wire [15:0] next_mixratio = ~i_reg_aspect_ratio ? 16'b0 : // no mixing for 1:1 pixel aspect ratio r_xs == 5'd3 ? {8'd110,8'd146} : r_xs == 5'd6 ? {8'd219,8'd37} : r_xs == 5'd10 ? {8'd73,8'd183} : @@ -242,9 +241,9 @@ reg overlay_active; // calc rgb value to hdmi always_ff @(posedge clk_pixel) begin reg [23:0] rgb_nes; - if (asp8x7_on && cx == 11'd198 || ~asp8x7_on && cx == 11'd253) + if (i_reg_aspect_ratio && cx == 11'd198 || ~i_reg_aspect_ratio && cx == 11'd253) active <= 1'b1; - if (asp8x7_on && cx == 11'd1075 || ~asp8x7_on && cx == 11'd1021) + if (i_reg_aspect_ratio && cx == 11'd1075 || ~i_reg_aspect_ratio && cx == 11'd1021) active <= 1'b0; if (cx == 11'd256 && cy >= 10'd24 && cy < 10'd696) overlay_active <= 1; @@ -257,7 +256,7 @@ always_ff @(posedge clk_pixel) begin r_active <= active; r2_active <= r_active; r_xs <= xs; r2_xs <= r_xs; if (active) begin - if (asp8x7_on) + if (i_reg_aspect_ratio) xs <= xs == 5'd23 ? 0 : xs + 1; else xs <= xs == 5'd20 ? 0 : xs + 1; @@ -274,7 +273,7 @@ always_ff @(posedge clk_pixel) begin // 2 - mix rgb and output if (r2_active) begin - if (asp8x7_on && mix) + if (i_reg_aspect_ratio && mix) rgb_nes = {rmix[15:8], gmix[15:8], bmix[15:8]}; else rgb_nes = rgbv; diff --git a/src/nestang_top.sv b/src/nestang_top.sv index 7f6773c..5a0e446 100644 --- a/src/nestang_top.sv +++ b/src/nestang_top.sv @@ -88,7 +88,7 @@ module nestang_top ( // Core settings wire arm_reset = 0; -wire [1:0] system_type = 2'b0; +wire [1:0] system_type; wire pal_video = 0; wire [1:0] scanlines = 2'b0; wire joy_swap = 0; @@ -200,7 +200,7 @@ always @(posedge clk) begin reset_cnt <= reset_cnt == 0 ? 0 : reset_cnt - 1; if (reset_cnt == 0) // if (reset_cnt == 0 && s1) // for nano - sys_resetn <= ~(joy1_btns[5] && joy1_btns[2]); // 8BitDo Home button = Select + Down + sys_resetn <= ~(joy1_btns[5] && joy1_btns[3]); // Select + Up end `ifndef VERILATOR @@ -237,8 +237,14 @@ assign fclk = sys_clk; wire [31:0] status; - // Main NES machine +wire [7:0] NES_memory_din_cpu; +wire [7:0] NES_cheats_otuput_data_test; +wire NES_top_cheats_stb; + +assign NES_top_cheats_stb= (NES_cheats_enabled)&&(NES_cheats_loaded)&&(NES_cheats_stb); +assign NES_memory_din_cpu = (!NES_top_cheats_stb ? memory_din_cpu : NES_cheats_otuput_data); + NES nes( .clk(clk), .reset_nes(reset_nes), .cold_reset(1'b0), .sys_type(system_type), .nes_div(nes_ce), @@ -252,7 +258,7 @@ NES nes( .cpumem_addr(memory_addr_cpu), .cpumem_read(memory_read_cpu), - .cpumem_din(memory_din_cpu), + .cpumem_din(NES_cheats_otuput_data), .cpumem_write(memory_write_cpu), .cpumem_dout(memory_dout_cpu), .ppumem_addr(memory_addr_ppu), @@ -267,7 +273,10 @@ NES nes( .int_audio(int_audio), // VRC6 .ext_audio(ext_audio), - .apu_ce(), .gg(), .gg_code(), .gg_avail(), .gg_reset(), .emphasis(), .save_written() + .apu_ce(), .gg(), .gg_code(), .gg_avail(), .gg_reset(), .emphasis(), .save_written(), + // Enhanced APU + .i_APU_enhancements_ce(NES_enhanced_APU), + .i_APU_mapper_saturates((NES_mapper == 8'h04)||(NES_mapper == 8'h45)) // Mapper4/MMC3 and Mapper69 saturate so far ); // loader_write -> clock when data available @@ -322,7 +331,9 @@ GameLoader loader( .mem_addr(loader_addr), .mem_data(loader_write_data), .mem_write(loader_write), .bios_download(), .mapper_flags(loader_flags), .busy(loader_busy), .done(loader_done), - .error(loader_fail), .rom_loaded() + .error(loader_fail), .rom_loaded(), + // APU Enhancement + .o_mapper(NES_mapper) ); assign int_audio = 1; @@ -363,6 +374,7 @@ nes2hdmi u_hdmi ( // purple: RGB=440064 (010001000_00000000_01100100), BGR5= .clk(clk), .resetn(sys_resetn), .color(color), .cycle(cycle), .scanline(scanline), .sample(sample >> 1), + .i_reg_aspect_ratio(NES_aspect_ratio), .overlay(overlay), .overlay_x(overlay_x), .overlay_y(overlay_y), .overlay_color(overlay_color), .clk_pixel(hclk), .clk_5x_pixel(hclk5), @@ -452,7 +464,8 @@ always @(posedge clk) begin // RV endcase end end - +reg NES_enhanced_APU; +reg [7:0] NES_mapper; iosys #(.COLOR_LOGO(15'b01100_00000_01000), .CORE_ID(1) ) // purple nestang logo iosys ( .clk(clk), .hclk(hclk), .resetn(sys_resetn), @@ -474,7 +487,33 @@ iosys #(.COLOR_LOGO(15'b01100_00000_01000), .CORE_ID(1) ) // purple nestang .uart_tx(UART_TXD), .uart_rx(UART_RXD), .sd_clk(sd_clk), .sd_cmd(sd_cmd), .sd_dat0(sd_dat0), .sd_dat1(sd_dat1), - .sd_dat2(sd_dat2), .sd_dat3(sd_dat3) + .sd_dat2(sd_dat2), .sd_dat3(sd_dat3), + .o_reg_enhanced_apu(NES_enhanced_APU), + // Wishbone master + .i_wb_ack(NES_wb_slave_ack), + .i_wb_stall(NES_wb_slave_stall), + .i_wb_idata(NES_wb_slave_data), + .i_wb_err(NES_wb_slave_err), + .o_wb_cyc(NES_wb_master_cyc), + .o_wb_stb(NES_wb_master_stb), + .o_wb_we(NES_wb_master_we), + .o_wb_err(NES_wb_master_err), + .o_wb_addr(NES_wb_slave_addr), + .o_wb_odata(NES_wb_master_data), + .o_wb_sel(NES_wb_master_sel), + + // Cheats + .o_cheats_enabled(NES_cheats_enabled), + .o_cheats_loaded(NES_cheats_loaded), + + // Debug LED + .o_dbg_led(), + + // System Type + .o_sys_type(system_type), + + // Aspect Ratio + .o_reg_aspect_ratio(NES_aspect_ratio) ); // Controller input @@ -554,10 +593,63 @@ assign joypad2_data[0] = joypad_bits2[0]; //assign led = ~{~UART_RXD, loader_done}; //assign led = ~{~UART_RXD, usb_conerr, loader_done}; -assign led = {joy1_btns[1], joy1_btns[0]}; +// assign led = {joy1_btns[1], joy1_btns[0]}; reg [23:0] led_cnt; always @(posedge clk) led_cnt <= led_cnt + 1; //assign led = {led_cnt[23], led_cnt[22]}; +// +// Wishbone Bus +// +wire NES_wb_master_cyc; +wire NES_wb_master_stb; +wire NES_wb_master_we; +wire NES_wb_master_err; +wire [1:0] NES_wb_slave_addr; +wire [128:0] NES_wb_master_data; +wire NES_wb_slave_ack; +wire NES_wb_slave_stall; +wire NES_wb_slave_err; +wire [128:0] NES_wb_slave_data; +wire [2:0] NES_wb_slave_sel; + +// Cheats +wire [7:0] NES_cheats_otuput_data; +wire NES_cheats_stb; +wire [23:0] NES_cheats_memory_addr_cpu; +wire NES_cheats_enabled; +wire NES_cheats_loaded; + +assign NES_cheats_memory_addr_cpu = {2'b00, memory_addr_cpu}; +// assign led[0] = ~NES_cheats_enabled; +// assign led[1] = ~NES_cheats_loaded; + +cheat_wizard( + .i_clk(clk), + .i_reset_n(sys_resetn), + .i_cheats_enabled(NES_cheats_enabled), + .i_cheats_loaded(NES_cheats_loaded), + .i_sram_address(NES_cheats_memory_addr_cpu), + .i_sram_data(memory_din_cpu), + .o_cheat_stb(NES_cheats_stb), + .o_sram_data(NES_cheats_otuput_data), + .i_wb_cyc(NES_wb_master_cyc), + .i_wb_stb(NES_wb_master_stb), + .i_wb_we(NES_wb_master_we), + .i_wb_err(NES_wb_master_err), + .i_wb_addr(NES_wb_slave_addr), + .i_wb_idata(NES_wb_master_data), + .o_wb_ack(NES_wb_slave_ack), + .o_wb_stall(NES_wb_slave_stall), + .o_wb_err(NES_wb_slave_err) +); + +// Aspect Ratio +reg NES_aspect_ratio; +initial NES_aspect_ratio = 1'b0; + +// assign led[0] = ~NES_cheats_enabled; +// assign led[1] = ~NES_cheats_loaded; + endmodule \ No newline at end of file diff --git a/src/wishbone_slave.sby b/src/wishbone_slave.sby new file mode 100644 index 0000000..0437ce4 --- /dev/null +++ b/src/wishbone_slave.sby @@ -0,0 +1,27 @@ +[tasks] +bound +prf +cvr + +[options] +bound: mode bmc +bound: depth 128 +prf: mode prove +prf: depth 64 +cvr: mode cover +cvr: depth 250 +cvr: append 20 + + +[engines] +bound: smtbmc +prf: smtbmc yices +cvr: smtbmc + +[script] +read_verilog -formal wishbone_slave.v +prep -top wishbone_slave + +[files] +wishbone_slave.v +wishbone_slaves.vh diff --git a/src/wishbone_slaves.vh b/src/wishbone_slaves.vh new file mode 100644 index 0000000..73c3a99 --- /dev/null +++ b/src/wishbone_slaves.vh @@ -0,0 +1,6 @@ +`ifndef _wishbone_slaves_vh_ +`define _wishbone_slaves_vh_ + +parameter WISHBONE_SLAVE_ADDRESS_CHEATS_DATA = 1 + +`endif \ No newline at end of file