From 0cf89076ad252fc2b802c3e1172f2fae65d746c0 Mon Sep 17 00:00:00 2001 From: Robert Peip Date: Tue, 15 Dec 2020 15:23:28 +0100 Subject: [PATCH 1/2] rearranged code to meet Verilog/VHDL standards add testbench --- rtl/gb.v | 94 +++--- rtl/gbc_snd.vhd | 6 +- rtl/link.v | 5 +- rtl/sprites.v | 13 +- rtl/video.v | 117 ++++---- sim/graeval.exe | Bin 0 -> 11264 bytes sim/readme.md | 48 +++ sim/src/gameboy/cheatcodes.sv | 15 + sim/src/mem/SyncFifo.vhd | 87 ++++++ sim/src/procbus/proc_bus.vhd | 277 +++++++++++++++++ sim/src/procbus/testprocessor.vhd | 386 ++++++++++++++++++++++++ sim/src/reg_map/reg_gameboy.vhd | 70 +++++ sim/src/rs232/rs232_receiver.vhd | 79 +++++ sim/src/rs232/rs232_transmitter.vhd | 69 +++++ sim/src/rs232/tbrs232_receiver.vhd | 40 +++ sim/src/rs232/tbrs232_transmitter.vhd | 46 +++ sim/src/tb/framebuffer.vhd | 108 +++++++ sim/src/tb/gb_bios.vhd | 285 ++++++++++++++++++ sim/src/tb/globals.vhd | 13 + sim/src/tb/sdram_model.vhd | 113 +++++++ sim/src/tb/stringprocessor.vhd | 389 ++++++++++++++++++++++++ sim/src/tb/tb.vhd | 263 ++++++++++++++++ sim/src/tb/tb_interpreter.vhd | 125 ++++++++ sim/tests/gb_bootrom.lua | 15 + sim/tests/luareg.lua | 44 +++ sim/tests/vsim_comm.lua | 417 ++++++++++++++++++++++++++ sim/vcom_all.bat | 55 ++++ sim/vmap_all.bat | 23 ++ sim/vsim_start.bat | 1 + 29 files changed, 3095 insertions(+), 108 deletions(-) create mode 100644 sim/graeval.exe create mode 100644 sim/readme.md create mode 100644 sim/src/gameboy/cheatcodes.sv create mode 100644 sim/src/mem/SyncFifo.vhd create mode 100644 sim/src/procbus/proc_bus.vhd create mode 100644 sim/src/procbus/testprocessor.vhd create mode 100644 sim/src/reg_map/reg_gameboy.vhd create mode 100644 sim/src/rs232/rs232_receiver.vhd create mode 100644 sim/src/rs232/rs232_transmitter.vhd create mode 100644 sim/src/rs232/tbrs232_receiver.vhd create mode 100644 sim/src/rs232/tbrs232_transmitter.vhd create mode 100644 sim/src/tb/framebuffer.vhd create mode 100644 sim/src/tb/gb_bios.vhd create mode 100644 sim/src/tb/globals.vhd create mode 100644 sim/src/tb/sdram_model.vhd create mode 100644 sim/src/tb/stringprocessor.vhd create mode 100644 sim/src/tb/tb.vhd create mode 100644 sim/src/tb/tb_interpreter.vhd create mode 100644 sim/tests/gb_bootrom.lua create mode 100644 sim/tests/luareg.lua create mode 100644 sim/tests/vsim_comm.lua create mode 100644 sim/vcom_all.bat create mode 100644 sim/vmap_all.bat create mode 100644 sim/vsim_start.bat diff --git a/rtl/gb.v b/rtl/gb.v index 9055e6f..89fbdc3 100644 --- a/rtl/gb.v +++ b/rtl/gb.v @@ -93,7 +93,8 @@ wire sel_zpram = (cpu_addr[15:7] == 9'b111111111) && // 127 bytes zero pageram a wire sel_audio = (cpu_addr[15:8] == 8'hff) && // audio reg ff10 - ff3f and ff76/ff77 PCM12/PCM34 (undocumented registers) ((cpu_addr[7:5] == 3'b001) || (cpu_addr[7:4] == 4'b0001) || (cpu_addr[7:0] == 8'h76) || (cpu_addr[7:0] == 8'h77)); -//DMA can select from $0000 to $F100 +//DMA can select from $0000 to $F100 +wire [15:0] dma_addr; wire dma_sel_rom = !dma_addr[15]; // lower 32k are rom wire dma_sel_cram = dma_addr[15:13] == 3'b101; // 8k cart ram at $a000 wire dma_sel_vram = dma_addr[15:13] == 3'b100; // 8k video ram at $8000 @@ -108,16 +109,46 @@ wire sel_key1 = cpu_addr == 16'hff4d; // KEY1 - CGB Mode Only - Prepar wire sel_rp = cpu_addr == 16'hff56; //FF56 - RP - CGB Mode Only - Infrared Communications Port //HDMA can select from $0000 to $7ff0 or A000-DFF0 +wire [15:0] hdma_source_addr; wire hdma_sel_rom = !hdma_source_addr[15]; // lower 32k are rom wire hdma_sel_cram = hdma_source_addr[15:13] == 3'b101; // 8k cart ram at $a000 wire hdma_sel_iram = hdma_source_addr[15:13] == 3'b110; // 8k internal ram at $c000-$dff0 // the boot roms sees a special $42 flag in $ff50 if it's supposed to to a fast boot +reg boot_rom_enabled; wire sel_fast = fast_boot && cpu_addr == 16'hff50 && boot_rom_enabled; +wire sc_start; +wire sc_shiftclock; wire [7:0] sc_r = {sc_start,6'h3F,sc_shiftclock}; + +wire irq_ack; +wire [7:0] irq_vec; +reg [4:0] if_r; +reg [4:0] ie_r; // writing $ffff sets the irq enable mask +wire irq_n; +reg [2:0] iram_bank; //1-7 FF70 - SVBK +reg vram_bank; //0-1 FF4F - VBK + +wire [7:0] hdma_do; +wire hdma_active; + +reg cpu_speed; // - 0 Normal mode (4MHz) - 1 Double Speed Mode (8MHz) +reg prepare_switch; // set to 1 to toggle speed + +wire [7:0] joy_do; +wire [7:0] sb_o; +wire [7:0] timer_do; +wire [7:0] video_do; +wire [7:0] audio_do; +wire [7:0] rom_do; +wire [7:0] vram_do; +wire [7:0] vram1_do; +wire [7:0] zpram_do; +wire [7:0] iram_do; + // http://gameboy.mongenel.com/dmg/asmmemmap.html wire [7:0] cpu_di = irq_ack?irq_vec: @@ -165,6 +196,8 @@ end wire cpu_stop; +wire genie_ovr; +wire [7:0] genie_data; GBse cpu ( .RESET_n ( !reset_r ), @@ -192,9 +225,6 @@ GBse cpu ( // --------------------------- Cheat Engine --------------------------- // -------------------------------------------------------------------- -wire genie_ovr; -wire [7:0] genie_data; - CODES codes ( .clk (clk_sys), .reset (gg_reset), @@ -211,8 +241,6 @@ CODES codes ( // -------------------------------------------------------------------- // --------------------- Speed Toggle KEY1 (GBC)----------------------- // -------------------------------------------------------------------- -reg cpu_speed; // - 0 Normal mode (4MHz) - 1 Double Speed Mode (8MHz) -reg prepare_switch; // set to 1 to toggle speed assign speed = cpu_speed; always @(posedge clk_sys) begin @@ -236,12 +264,11 @@ end wire audio_rd = !cpu_rd_n && sel_audio; wire audio_wr = !cpu_wr_n && sel_audio; -wire [7:0] audio_do; gbc_snd audio ( .clk ( clk_sys ), .ce ( ce_2x ), - .reset ( reset_r ), + .reset ( reset_r ), .is_gbc ( isGBC ), @@ -261,9 +288,6 @@ gbc_snd audio ( // SNAC wire serial_irq; -wire [7:0] sb_o; -wire sc_start; -wire sc_shiftclock; assign sc_int_clock2 = sc_shiftclock; @@ -302,7 +326,7 @@ always @(posedge clk_cpu) begin else if(sel_joy && !cpu_wr_n) p54 <= cpu_do[5:4]; end -wire [7:0] joy_do = { 2'b11, p54, joy_din }; +assign joy_do = { 2'b11, p54, joy_din }; assign joy_p54 = p54; // -------------------------------------------------------------------- @@ -313,10 +337,10 @@ assign joy_p54 = p54; // the register to 1. The "highest" one active is cleared when the cpu // runs an interrupt ack cycle or when it writes a 0 to the register -wire irq_ack = !cpu_iorq_n && !cpu_m1_n; +assign irq_ack = !cpu_iorq_n && !cpu_m1_n; // irq vector -wire [7:0] irq_vec = +assign irq_vec = if_r[0]&&ie_r[0]?8'h40: // vsync if_r[1]&&ie_r[1]?8'h48: // lcdc if_r[2]&&ie_r[2]?8'h50: // timer @@ -329,15 +353,15 @@ wire [7:0] irq_vec = reg [3:0] inputD, inputD2; // irq is low when an enable irq is active -wire irq_n = !(ie_r & if_r); +assign irq_n = !(ie_r & if_r); -reg [4:0] if_r; -reg [4:0] ie_r; // writing $ffff sets the irq enable mask +wire video_irq,vblank_irq; +wire timer_irq; reg old_vblank_irq, old_video_irq, old_timer_irq, old_serial_irq; +reg old_ack = 0; always @(negedge clk_cpu) begin //negedge to trigger interrupt earlier - reg old_ack = 0; - + if(reset_r) begin ie_r <= 5'h00; if_r <= 5'h00; @@ -391,8 +415,6 @@ end // ------------------------------ timer ------------------------------- // -------------------------------------------------------------------- -wire timer_irq; -wire [7:0] timer_do; timer timer ( .reset ( reset_r ), .clk ( clk_cpu ), //2x in fast mode @@ -411,10 +433,7 @@ timer timer ( // -------------------------------------------------------------------- // cpu tries to read or write the lcd controller registers -wire video_irq,vblank_irq; -wire [7:0] video_do; wire [12:0] video_addr; -wire [15:0] dma_addr; wire video_rd, dma_rd; wire [7:0] dma_data = dma_sel_iram?iram_do:dma_sel_vram?(isGBC&&vram_bank)?vram1_do:vram_do:cart_do; @@ -460,9 +479,8 @@ video video ( // total 8k/16k (CGB) vram from $8000 to $9fff wire cpu_wr_vram = sel_vram && !cpu_wr_n && lcd_mode!=3; -reg vram_bank; //0-1 FF4F - VBK - -wire [7:0] vram_do,vram1_do; +wire hdma_rd; +wire is_hdma_cart_addr; wire [7:0] vram_di = (hdma_rd&&isGBC)? hdma_sel_iram?iram_do: is_hdma_cart_addr?cart_do: @@ -473,6 +491,7 @@ wire [7:0] vram_di = (hdma_rd&&isGBC)? wire vram_wren = video_rd?1'b0:!vram_bank&&((hdma_rd&&isGBC)||cpu_wr_vram); wire vram1_wren = video_rd?1'b0:vram_bank&&((hdma_rd&&isGBC)||cpu_wr_vram); +wire [15:0] hdma_target_addr; wire [12:0] vram_addr = video_rd?video_addr:(hdma_rd&&isGBC)?hdma_target_addr[12:0]:(dma_rd&&dma_sel_vram)?dma_addr[12:0]:cpu_addr[12:0]; @@ -505,12 +524,6 @@ end // -------------------------- HDMA engine(GBC) ------------------------ // -------------------------------------------------------------------- -wire [15:0] hdma_source_addr; -wire [15:0] hdma_target_addr; -wire [7:0] hdma_do; -wire hdma_rd; -wire hdma_active; - hdma hdma( .reset ( reset_r ), .clk ( clk_sys ), @@ -540,7 +553,6 @@ hdma hdma( // 127 bytes internal zero page ram from $ff80 to $fffe wire cpu_wr_zpram = sel_zpram && !cpu_wr_n; -wire [7:0] zpram_do; spram #(7) zpram ( .clock ( clk_cpu ), .address ( cpu_addr[6:0] ), @@ -553,7 +565,7 @@ spram #(7) zpram ( // ------------------------ 8k/32k(GBC) internal ram ----------------- // -------------------------------------------------------------------- -reg [2:0] iram_bank; //1-7 FF70 - SVBK +wire cpu_wr_iram = sel_iram && !cpu_wr_n; wire iram_wren = (dma_rd&&dma_sel_iram)||(isGBC&&hdma_rd&&hdma_sel_iram)?1'b0:cpu_wr_iram; wire [14:0] iram_addr = (isGBC&&hdma_rd&&hdma_sel_iram)? //hdma transfer? @@ -567,8 +579,6 @@ wire [14:0] iram_addr = (isGBC&&hdma_rd&&hdma_sel_iram)? //hdma tr {3'd0,cpu_addr[11:0]}; //bank 0 -wire cpu_wr_iram = sel_iram && !cpu_wr_n; -wire [7:0] iram_do; spram #(15) iram ( .clock ( clk_cpu ), .address ( iram_addr ), @@ -594,7 +604,6 @@ end // -------------------------------------------------------------------- // writing 01(GB) or 11(GBC) to $ff50 disables the internal rom -reg boot_rom_enabled; always @(posedge clk) begin if(reset_r) boot_rom_enabled <= 1'b1; @@ -605,7 +614,10 @@ end // combine boot rom data with cartridge data -wire [7:0] rom_do = isGBC? //GameBoy Color? +wire [7:0] boot_rom_do; +wire [7:0] fast_boot_rom_do; + +assign rom_do = isGBC? //GameBoy Color? (((cpu_addr[14:8] == 7'h00) || (hdma_rd&& hdma_source_addr[14:8] == 7'h00))&& boot_rom_enabled)?gbc_bios_do: //0-FF bootrom 1st part ((cpu_addr[14:9] == 6'h00) || (hdma_rd&& hdma_source_addr[14:9] == 6'h00))? cart_do: //100-1FF Cart Header (((cpu_addr[14:12] == 3'h0) || (hdma_rd&& hdma_source_addr[14:12] == 3'h0)) && boot_rom_enabled)?gbc_bios_do: //200-8FF bootrom 2nd part @@ -614,7 +626,7 @@ wire [7:0] rom_do = isGBC? //GameBoy Color? wire is_dma_cart_addr = (dma_sel_rom || dma_sel_cram); //rom or external ram -wire is_hdma_cart_addr = (hdma_sel_rom || hdma_sel_cram); //rom or external ram +assign is_hdma_cart_addr = (hdma_sel_rom || hdma_sel_cram); //rom or external ram assign cart_di = cpu_do; assign cart_addr = (isGBC&&hdma_rd&&is_hdma_cart_addr)?hdma_source_addr:(dma_rd&&is_dma_cart_addr)?dma_addr:cpu_addr; @@ -623,14 +635,12 @@ assign cart_wr = (sel_rom || sel_cram) && !cpu_wr_n && !hdma_rd; assign gbc_bios_addr = hdma_rd?hdma_source_addr[11:0]:cpu_addr[11:0]; -wire [7:0] boot_rom_do; boot_rom boot_rom ( .addr ( cpu_addr[7:0] ), .clk ( clk_sys ), .data ( boot_rom_do ) ); -wire [7:0] fast_boot_rom_do; fast_boot_rom fast_boot_rom ( .addr ( cpu_addr[7:0] ), .clk ( clk_sys ), diff --git a/rtl/gbc_snd.vhd b/rtl/gbc_snd.vhd index 0b9998e..ca7f724 100644 --- a/rtl/gbc_snd.vhd +++ b/rtl/gbc_snd.vhd @@ -602,6 +602,7 @@ begin when "1111" => -- FF3F 0000 1111 Samples 30 and 31 wav_ram(30) <= s1_writedata(7 downto 4); wav_ram(31) <= s1_writedata(3 downto 0); + when others => null; end case; end if; @@ -770,6 +771,7 @@ begin s1_readdata <= wav_ram(28) & wav_ram(29); when "1111" => -- FF3F 0000 1111 Samples 30 and 31 s1_readdata <= wav_ram(30) & wav_ram(31); + when others => null; end case; else s1_readdata <= X"FF"; @@ -991,7 +993,7 @@ begin acc_fcnt := ('0' & sq1_fcnt) + to_unsigned(1, acc_fcnt'length); if acc_fcnt(acc_fcnt'high) = '1' then sq1_suppressed <= '0'; - sq1_phase := sq1_phase + 1; + if (sq1_phase < 7) then sq1_phase := sq1_phase + 1; else sq1_phase := 0; end if; sq1_fcnt := unsigned(sq1_freq); sq1_sduty := sq1_duty; -- only change duty after the sample is finished else @@ -1212,7 +1214,7 @@ begin acc_fcnt := ('0' & sq2_fcnt) + to_unsigned(1, acc_fcnt'length); if acc_fcnt(acc_fcnt'high) = '1' then sq2_suppressed <= '0'; - sq2_phase := sq2_phase + 1; + if (sq2_phase < 7) then sq2_phase := sq2_phase + 1; else sq2_phase := 0; end if; sq2_fcnt := unsigned(sq2_freq); sq2_sduty := sq2_duty; -- only change duty after the sample is finished else diff --git a/rtl/link.v b/rtl/link.v index 7bf7ca0..57bf425 100644 --- a/rtl/link.v +++ b/rtl/link.v @@ -24,20 +24,19 @@ module link #( output reg sc_int_clock ); +reg [7:0] sb_r = 0; assign sb = sb_r; reg [3:0] serial_counter; -reg [7:0] sb_r = 0; - reg serial_out_r = 0; assign serial_data_out = serial_out_r; reg serial_clk_out_r = 1; assign serial_clk_out = serial_clk_out_r; -assign serial_irq = serial_irq_r; reg serial_irq_r; +assign serial_irq = serial_irq_r; reg [8:0] serial_clk_div; //8192Hz diff --git a/rtl/sprites.v b/rtl/sprites.v index 2753a34..146b722 100644 --- a/rtl/sprites.v +++ b/rtl/sprites.v @@ -56,18 +56,19 @@ module sprites ( localparam SPRITES_PER_LINE = 10; +reg [7:0] oam_spr_addr; +wire [7:0] oam_fetch_addr; +reg [7:0] oam_q; wire [7:0] oam_addr = dma_active ? oam_addr_in : oam_eval ? oam_spr_addr : oam_fetch ? oam_fetch_addr : oam_addr_in; - -reg [7:0] oam_spr_addr; + wire valid_oam_addr = (oam_addr[7:4] < 4'hA); // $FEA0 - $FEFF unused range assign oam_do = dma_active ? 8'hFF : valid_oam_addr ? oam_q : 8'd0; reg [7:0] oam_data[0:159]; -reg [7:0] oam_q; always @(posedge clk) begin if (ce_cpu) begin if(oam_wr && valid_oam_addr) begin @@ -93,6 +94,8 @@ wire sprite_on_line = (v_cnt + 8'd16 >= spr_y) && (v_cnt + 8'd16 < spr_y + spr_h assign oam_eval_end = (spr_index == 6'd40); +wire [0:9] sprite_x_matches; + reg old_fetch_done; always @(posedge clk) begin if (ce) begin @@ -159,7 +162,7 @@ end // Sprite fetching -wire [0:9] sprite_x_matches = { +assign sprite_x_matches = { sprite_x[0] == h_cnt, sprite_x[1] == h_cnt, sprite_x[2] == h_cnt, @@ -192,7 +195,7 @@ wire [5:0] oam_fetch_index = sprite_no[active_sprite]; reg [3:0] row; reg [7:0] tile_no; reg oam_fetch_cycle; -wire [7:0] oam_fetch_addr = {oam_fetch_index, 1'b1, oam_fetch_cycle}; +assign oam_fetch_addr = {oam_fetch_index, 1'b1, oam_fetch_cycle}; assign sprite_addr = size16 ? {tile_no[7:1],row} : {tile_no,row[2:0]}; always @(posedge clk) begin diff --git a/rtl/video.v b/rtl/video.v index 40ec801..8c88378 100644 --- a/rtl/video.v +++ b/rtl/video.v @@ -67,45 +67,18 @@ wire [7:0] sprite_attr; wire [3:0] sprite_index; wire oam_eval_end; -sprites sprites ( - .clk ( clk ), - .ce ( ce ), - .ce_cpu ( ce_cpu ), - .size16 ( lcdc_spr_siz ), - .isGBC ( isGBC ), - .sprite_en( lcdc_spr_ena ), - .lcd_on ( lcd_on ), - .v_cnt ( v_cnt ), - .h_cnt ( pcnt ), - - .oam_eval ( oam ), - .oam_fetch ( mode3 ), - .oam_eval_reset ( end_of_line & ~vblank ), - .oam_eval_end ( oam_eval_end ), - - .sprite_fetch (sprite_found), - .sprite_addr ( sprite_addr ), - .sprite_attr ( sprite_attr ), - .sprite_index ( sprite_index ), - .sprite_fetch_done ( sprite_fetch_done) , - - .dma_active ( dma_active), - .oam_wr ( oam_wr ), - .oam_addr_in( oam_addr ), - .oam_di ( oam_di ), - .oam_do ( oam_do ) -); +reg dma_active; +reg [7:0] dma; +reg [9:0] dma_cnt; // dma runs 4*160 clock cycles = 160us @ 4MHz // give dma access to oam wire [7:0] oam_addr = dma_active?dma_addr[7:0]:cpu_addr; wire oam_wr = dma_active?(dma_cnt[1:0] == 2):(cpu_wr && cpu_sel_oam && !(mode==3 || mode==2)); wire [7:0] oam_di = dma_active?dma_data:cpu_di; - -assign lcd_on = lcdc_on; - // $ff40 LCDC +reg [7:0] lcdc; wire lcdc_on = lcdc[7]; wire lcdc_win_tile_map_sel = lcdc[6]; wire lcdc_win_ena = lcdc[5]; @@ -120,7 +93,7 @@ wire lcdc_spr_ena = lcdc[1]; wire lcdc_bg_ena = lcdc[0] | (isGBC&&isGBC_game); wire lcdc_bg_prio = lcdc[0]; -reg [7:0] lcdc; +assign lcd_on = lcdc_on; // ff41 STAT reg [7:0] stat; @@ -130,11 +103,13 @@ reg [7:0] scy; reg [7:0] scx; // ff44 line counter +reg [8:0] h_cnt; // max 455 +reg [7:0] v_cnt; // max 153 wire [7:0] ly = v_cnt; // ff45 line counter compare -wire lyc_match = (ly == lyc); reg [7:0] lyc; +wire lyc_match = (ly == lyc); reg [7:0] bgp; reg [7:0] obp0; @@ -165,9 +140,6 @@ reg[7:0] obpd [63:0]; //64 bytes assign dma_addr = { dma, dma_cnt[9:2] }; assign dma_rd = dma_active; -reg dma_active; -reg [7:0] dma; -reg [9:0] dma_cnt; // dma runs 4*160 clock cycles = 160us @ 4MHz always @(posedge clk) begin if(reset) dma_active <= 1'b0; @@ -232,6 +204,7 @@ always @(posedge clk) begin end end +wire pcnt_end; wire mode3_end = isGBC ? pcnt_end : (~sprite_found & pcnt_end); reg mode3_end_l; @@ -365,8 +338,10 @@ assign cpu_do = reg skip_en; reg [7:0] skip; reg [7:0] pcnt; +wire sprite_fetch_hold; +wire bg_shift_empty; -wire pcnt_end = ( pcnt == (isGBC ? 8'd168 : 8'd167) ); +assign pcnt_end = ( pcnt == (isGBC ? 8'd168 : 8'd167) ); wire pcnt_reset = end_of_line & ~vblank; always @(posedge clk) begin if (!lcd_on) begin @@ -400,9 +375,6 @@ always @(posedge clk) begin end end -reg [8:0] h_cnt; // max 455 -reg [7:0] v_cnt; // max 153 - // vcnt_reset goes high a few cycles after v_cnt is incremented to 153. // It resets v_cnt back to 0 and keeps it in reset until the following line. // This results in v_cnt 0 lasting for almost 2 lines. @@ -440,17 +412,21 @@ wire [7:0] bg_col = pcnt + scx; wire [9:0] bg_map_addr = {bg_line[7:3], bg_col[7:3]}; reg [4:0] win_col; +reg [7:0] win_line; +wire window_ena; +wire bg_fetch_done; +wire bg_reload_shift; + wire [9:0] win_map_addr = {win_line[7:3], win_col[4:0]}; wire [9:0] bg_tile_map_addr = window_ena ? win_map_addr : bg_map_addr; -reg [7:0] win_line; wire [2:0] tile_line = window_ena ? win_line[2:0] : bg_line[2:0]; reg window_match, window_ena_d; wire win_start = mode3 && lcdc_win_ena && ~sprite_fetch_hold && ~skip_en && ~bg_shift_empty && (v_cnt >= wy) && (pcnt == wx) && (wx < 8'hA7); -wire window_ena = window_match & ~pcnt_reset & lcdc_win_ena; +assign window_ena = window_match & ~pcnt_reset & lcdc_win_ena; always @(posedge clk) begin @@ -491,7 +467,6 @@ always @(posedge clk) begin end end - // -------------------------------------------------------------------- // ------------------- bg, window and sprite fetch ------------------- // -------------------------------------------------------------------- @@ -531,15 +506,15 @@ wire [7:0] bg_vram_data_in = (isGBC & isGBC_game & bg_tile_attr_new[5]) ? bit_re reg [2:0] bg_fetch_cycle; reg [2:0] sprite_fetch_cycle; -wire bg_fetch_done = (bg_fetch_cycle >= 3'd5); +assign bg_fetch_done = (bg_fetch_cycle >= 3'd5); wire sprite_fetch_done = (sprite_fetch_hold && sprite_fetch_cycle >= 3'd5); // The first B01 cycle does not fetch sprites so wait until the bg shift register is not empty -wire sprite_fetch_hold = sprite_found & ~bg_shift_empty; +assign sprite_fetch_hold = sprite_found & ~bg_shift_empty; reg [3:0] bg_shift_cnt; -wire bg_shift_empty = (bg_shift_cnt == 0); -wire bg_reload_shift = (bg_shift_cnt <= 1); +assign bg_shift_empty = (bg_shift_cnt == 0); +assign bg_reload_shift = (bg_shift_cnt <= 1); wire spr_prio = sprite_attr[7]; wire spr_attr_h_flip = sprite_attr[5]; @@ -566,6 +541,13 @@ wire [7:0] spr_cgb_index_prio = spr_cgb_prio(spr_cgb_index_shift[3], spr_cgb_ind // CGB will mask the old pixel to 0 if the new pixel has higher priority. wire [7:0] spr_tile_mask = (spr_tile_shift_0 | spr_tile_shift_1) & ((isGBC & isGBC_game) ? ~spr_cgb_index_prio : 8'hFF); +// cycle through the B01s states +wire bg_tile_map_rd = (mode3 && bg_fetch_cycle[2:1] == 2'b00); +wire bg_tile_data0_rd = (mode3 && bg_fetch_cycle[2:1] == 2'b01); +wire bg_tile_data1_rd = (mode3 && bg_fetch_cycle[2:1] == 2'b10); +wire bg_tile_obj0_rd = (mode3 && sprite_fetch_cycle[2:1] == 2'b01); +wire bg_tile_obj1_rd = (mode3 && sprite_fetch_cycle[2:1] == 2'b10); + always @(posedge clk) begin if (ce) begin @@ -656,13 +638,6 @@ always @(posedge clk) begin end -// cycle through the B01s states -wire bg_tile_map_rd = (mode3 && bg_fetch_cycle[2:1] == 2'b00); -wire bg_tile_data0_rd = (mode3 && bg_fetch_cycle[2:1] == 2'b01); -wire bg_tile_data1_rd = (mode3 && bg_fetch_cycle[2:1] == 2'b10); -wire bg_tile_obj0_rd = (mode3 && sprite_fetch_cycle[2:1] == 2'b01); -wire bg_tile_obj1_rd = (mode3 && sprite_fetch_cycle[2:1] == 2'b10); - assign vram_rd = lcdc_on && (bg_tile_map_rd || bg_tile_data0_rd || bg_tile_data1_rd || bg_tile_obj0_rd || bg_tile_obj1_rd); @@ -680,6 +655,36 @@ assign vram_addr = bg_tile_obj0_rd ? {1'b0, sprite_addr, 1'b0} : {1'b0, sprite_addr, 1'b1}; +sprites sprites ( + .clk ( clk ), + .ce ( ce ), + .ce_cpu ( ce_cpu ), + .size16 ( lcdc_spr_siz ), + .isGBC ( isGBC ), + .sprite_en( lcdc_spr_ena ), + .lcd_on ( lcd_on ), + + .v_cnt ( v_cnt ), + .h_cnt ( pcnt ), + + .oam_eval ( oam ), + .oam_fetch ( mode3 ), + .oam_eval_reset ( end_of_line & ~vblank ), + .oam_eval_end ( oam_eval_end ), + + .sprite_fetch (sprite_found), + .sprite_addr ( sprite_addr ), + .sprite_attr ( sprite_attr ), + .sprite_index ( sprite_index ), + .sprite_fetch_done ( sprite_fetch_done) , + + .dma_active ( dma_active), + .oam_wr ( oam_wr ), + .oam_addr_in( oam_addr ), + .oam_di ( oam_di ), + .oam_do ( oam_do ) +); + // -------------------------------------------------------------------- // ----------------------- lcd output stage ------------------------- // -------------------------------------------------------------------- @@ -693,6 +698,8 @@ reg sprite_pixel_visible; wire [1:0] sprite_pixel_data = {spr_tile_shift_1[7], spr_tile_shift_0[7]}; wire sprite_pixel_prio = spr_prio_shift[7]; +wire [1:0] bg_pix_data = { tile_shift_1[7], tile_shift_0[7] }; + always @(*) begin sprite_pixel_visible = 1'b0; if (|sprite_pixel_data && lcdc_spr_ena) begin // pixel active and sprites enabled @@ -722,8 +729,6 @@ always @(*) begin end -wire [1:0] bg_pix_data = { tile_shift_1[7], tile_shift_0[7] }; - // If lcdc_bg_ena is off in Non-GBC mode then both background and window are blank. (BG color 0) wire [1:0] bgp_data = (!lcdc_bg_ena || bg_pix_data == 2'b00) ? bgp[1:0] : (bg_pix_data == 2'b01) ? bgp[3:2] : diff --git a/sim/graeval.exe b/sim/graeval.exe new file mode 100644 index 0000000000000000000000000000000000000000..2fc283742c641b652d48d21b00da2a5ee6d5ec68 GIT binary patch literal 11264 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 literal 0 HcmV?d00001 diff --git a/sim/readme.md b/sim/readme.md new file mode 100644 index 0000000..47dfd2a --- /dev/null +++ b/sim/readme.md @@ -0,0 +1,48 @@ +# Purpose of this document + +This readme shall deliver a small introduction on what the simulation can be used for and how to simulate the GB core. + +Requirements: Modelsim or compatible Simulator. Windows 7/10 for viewing gpu output. +Tested Version: Modelsim 10.5 + +# Available features + +The simulation framework allows: +- running ROM like on real hardware + +Debugging options: +- waveform viewer in modelsim +- live graphical output + +Speed: +1 second realtime(FPGA) will take in the range of 5 Minutes in simulation. So don't expect to run deep into a game. + +# Shortcoming + +- The HPS Framework/Top level is not simulated at all, Cart download is done through a debug interface +- Sound cannot be checked other than viewing waveform + +# How to start + +- run sim/vmap_all.bat +- run sim/vcom_all.bat +- run sim/vsim_start.bat + +Simualtion will now open. +- Start it with "Run All" button or command +- go with a cmd tool into sim/tests +- run "lua gb_bootrom.lua" + +Test is now running. Watch command line or waveform. + +# Debug graphic + +In the sim folder, there is a "graeval.exe" +When the simulation has run for a while and the file "gra_fb_out.gra" exists and the size is not zero, +you can pull this file onto the graeval.exe +A new window will open and draw everything the core outputs in simulation. + +# How to simulate a specific ROM + +Change the path to the ROM in the luascript "sim/tests/gb_bootrom.lua" +As the script and the simulator run in different paths, you may have to change the path or copy the file locally. \ No newline at end of file diff --git a/sim/src/gameboy/cheatcodes.sv b/sim/src/gameboy/cheatcodes.sv new file mode 100644 index 0000000..b109251 --- /dev/null +++ b/sim/src/gameboy/cheatcodes.sv @@ -0,0 +1,15 @@ +module CODES( + input clk, + input reset, + input enable, + output available, + input [15:0] addr_in, + input [7:0] data_in, + input [128:0] code, + output genie_ovr, + output [7:0] genie_data +); + +assign genie_ovr = 0; + +endmodule diff --git a/sim/src/mem/SyncFifo.vhd b/sim/src/mem/SyncFifo.vhd new file mode 100644 index 0000000..3df0f09 --- /dev/null +++ b/sim/src/mem/SyncFifo.vhd @@ -0,0 +1,87 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use ieee.math_real.all; + +entity SyncFifo is + generic + ( + SIZE : integer; + DATAWIDTH : integer; + NEARFULLDISTANCE : integer + ); + port + ( + clk : in std_logic; + reset : in std_logic; + + Din : in std_logic_vector(DATAWIDTH - 1 downto 0); + Wr : in std_logic; + Full : out std_logic; + NearFull : out std_logic; + + Dout : out std_logic_vector(DATAWIDTH - 1 downto 0); + Rd : in std_logic; + Empty : out std_logic + ); +end SyncFifo; + +architecture arch of SyncFifo is + + constant SIZEBITS : integer := integer(ceil(log2(real(SIZE)))); + + type t_memory is array(0 to SIZE - 1) of std_logic_vector(DATAWIDTH - 1 downto 0); + signal memory : t_memory; + + signal wrcnt : unsigned(SIZEBITS - 1 downto 0) := (others => '0'); + signal rdcnt : unsigned(SIZEBITS - 1 downto 0) := (others => '0'); + + signal fifocnt : unsigned(SIZEBITS - 1 downto 0) := (others => '0'); + + signal full_wire : std_logic; + signal empty_wire : std_logic; + +begin + + full_wire <= '1' when rdcnt = wrcnt+1 else '0'; + empty_wire <= '1' when rdcnt = wrcnt else '0'; + + process(clk) + begin + if rising_edge(clk) then + if (reset = '1') then + wrcnt <= (others => '0'); + rdcnt <= (others => '0'); + fifocnt <= (others => '0'); + else + if (Wr = '1' and full_wire = '0') then + if (Rd = '0' or empty_wire = '1') then + fifocnt <= fifocnt + 1; + end if; + elsif (Rd = '1' and empty_wire = '0') then + fifocnt <= fifocnt - 1; + end if; + + if (fifocnt < NEARFULLDISTANCE) then + NearFull <= '0'; + else + NearFull <= '1'; + end if; + + if (Wr = '1' and full_wire = '0') then + memory(to_integer(wrcnt)) <= Din; + wrcnt <= wrcnt+1; + end if; + + if (Rd = '1' and empty_wire = '0') then + Dout <= memory(to_integer(rdcnt)); + rdcnt <= rdcnt+1; + end if; + end if; + end if; + end process; + + Full <= full_wire; + Empty <= empty_wire; + +end architecture; \ No newline at end of file diff --git a/sim/src/procbus/proc_bus.vhd b/sim/src/procbus/proc_bus.vhd new file mode 100644 index 0000000..f3673d6 --- /dev/null +++ b/sim/src/procbus/proc_bus.vhd @@ -0,0 +1,277 @@ +----------------------------------------------------------------- +--------------- Proc Bus Package -------------------------------- +----------------------------------------------------------------- + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +package pProc_bus is + + constant proc_buswidth : integer := 32; + constant proc_busadr : integer := 26; + + constant proc_buscount : integer := 15; + + constant proc_mpu_bits : integer := 16; + + type proc_bus_type is record + Din : std_logic_vector(proc_buswidth-1 downto 0); + Dout : std_logic_vector(proc_buswidth-1 downto 0); + Adr : std_logic_vector(proc_busadr-1 downto 0); + rnw : std_logic; + ena : std_logic; + done : std_logic; + end record; + + type tBusArray_Din is array(0 to proc_buscount - 1) of std_logic_vector(proc_buswidth-1 downto 0); + type tBusArray_Dout is array(0 to proc_buscount - 1) of std_logic_vector(proc_buswidth-1 downto 0); + type tBusArray_Adr is array(0 to proc_buscount - 1) of std_logic_vector(proc_busadr-1 downto 0); + type tBusArray_rnw is array(0 to proc_buscount - 1) of std_logic; + type tBusArray_ena is array(0 to proc_buscount - 1) of std_logic; + type tBusArray_done is array(0 to proc_buscount - 1) of std_logic; + + type tMPUArray is array(0 to proc_buscount - 1) of std_logic_vector(proc_mpu_bits-1 downto 0); + +end package; + + +----------------------------------------------------------------- +--------------- Reg Map Package -------------------------------- +----------------------------------------------------------------- + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +library work; +use work.pProc_bus.all; + +package pRegmap is + + type regaccess_type is + ( + readwrite, + readonly, + pulse, + writeonly + ); + + type regmap_type is record + Adr : integer range 0 to (2**proc_busadr)-1; + upper : integer range 0 to proc_buswidth-1; + lower : integer range 0 to proc_buswidth-1; + size : integer range 0 to (2**proc_busadr)-1; + default : integer; + acccesstype : regaccess_type; + end record; + +end package; + + +----------------------------------------------------------------- +--------------- Converter Bus -> Processor ---------------------- +----------------------------------------------------------------- + + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +library work; +use work.pProc_bus.all; + +entity eProc_bus is + port + ( + proc_din : out std_logic_vector(proc_buswidth-1 downto 0); + proc_dout : in std_logic_vector(proc_buswidth-1 downto 0); + proc_adr : in std_logic_vector(proc_busadr-1 downto 0); + proc_rnw : in std_logic; + proc_ena : in std_logic; + proc_done : out std_logic; + + proc_bus : inout proc_bus_type := ((others => 'Z'), (others => 'Z'), (others => 'Z'), 'Z', 'Z', 'Z') + ); +end entity; + +architecture arch of eProc_bus is + +begin + + proc_din <= proc_bus.Dout; + proc_done <= proc_bus.done; + + proc_bus.adr <= proc_adr; + proc_bus.rnw <= proc_rnw; + proc_bus.ena <= proc_ena; + proc_bus.Din <= proc_dout; + + -- prevent simulation warnings + proc_bus.Dout <= (others => 'Z'); + +end architecture; + +----------------------------------------------------------------- +--------------- Reg Interface ----------------------------------- +----------------------------------------------------------------- + + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +library work; +use work.pProc_bus.all; +use work.pRegmap.all; + +entity eProcReg is + generic + ( + Reg : regmap_type; + index : integer := 0 + ); + port + ( + clk : in std_logic; + proc_bus : inout proc_bus_type := ((others => 'Z'), (others => 'Z'), (others => 'Z'), 'Z', 'Z', 'Z'); + Din : in std_logic_vector(Reg.upper downto Reg.lower); + Dout : out std_logic_vector(Reg.upper downto Reg.lower); + written : out std_logic := '0' + ); +end entity; + +architecture arch of eProcReg is + + signal Dout_buffer : std_logic_vector(Reg.upper downto Reg.lower) := std_logic_vector(to_unsigned(Reg.default,Reg.upper-Reg.lower+1)); + + signal Adr : std_logic_vector(proc_bus.adr'left downto 0); + +begin + + Adr <= std_logic_vector(to_unsigned(Reg.Adr + index, proc_bus.adr'length)); + + greadwrite : if (Reg.acccesstype = readwrite or Reg.acccesstype = writeonly) generate + begin + + process (clk) + begin + if rising_edge(clk) then + + if (proc_bus.Adr = Adr and proc_bus.rnw = '0' and proc_bus.ena = '1') then + Dout_buffer <= proc_bus.Din(Reg.upper downto Reg.lower); + written <= '1'; + else + written <= '0'; + end if; + + end if; + end process; + end generate; + + gpulse : if (Reg.acccesstype = pulse) generate + begin + + process (clk) + begin + if rising_edge(clk) then + + Dout_buffer <= (others => '0'); + if (proc_bus.Adr = Adr and proc_bus.rnw = '0' and proc_bus.ena = '1') then + Dout_buffer <= proc_bus.Din(Reg.upper downto Reg.lower); + written <= '1'; + else + written <= '0'; + end if; + + end if; + end process; + end generate; + + Dout <= Dout_buffer; + + proc_bus.Dout <= (others => 'Z'); + + goutput : if (Reg.acccesstype = readwrite or Reg.acccesstype = readonly) generate + begin + proc_bus.Dout(Reg.upper downto Reg.lower) <= Din when proc_bus.Adr = Adr else (others => 'Z'); + end generate; + + proc_bus.done <= '1' when proc_bus.Adr = Adr else 'Z'; + + -- prevent simulation warnings + proc_bus.Adr <= (others => 'Z'); + proc_bus.Din <= (others => 'Z'); + proc_bus.rnw <= 'Z'; + + +end architecture; + + + +----------------------------------------------------------------- +--------------- Address enable --------------------------------- +----------------------------------------------------------------- + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +library work; +use work.pProc_bus.all; +use work.pRegmap.all; + +entity eProcAddrEna is + generic + ( + Reg : regmap_type + ); + port + ( + clk : in std_logic; + proc_bus : inout proc_bus_type := ((others => 'Z'), (others => 'Z'), (others => 'Z'), 'Z', 'Z', 'Z'); + ena : out std_logic; + addr : out std_logic_vector(proc_busadr-1 downto 0) + ); +end entity; + +architecture arch of eProcAddrEna is + + signal proc_adr_buf :std_logic_vector(proc_busadr-1 downto 0) := (others => '0'); + + signal calc_addr : std_logic_vector(proc_busadr-1 downto 0); + signal calc_ena : std_logic; + +begin + + + + calc_addr <= std_logic_vector(unsigned(proc_adr_buf) - to_unsigned(Reg.Adr, proc_busadr)); + calc_ena <= '1' when to_integer(unsigned(proc_adr_buf)) >= Reg.Adr and to_integer(unsigned(proc_adr_buf)) < (Reg.Adr + Reg.size) else '0'; + + process (clk) + begin + if rising_edge(clk) then + + proc_adr_buf <= proc_bus.Adr; + + if (to_integer(unsigned(calc_addr)) < Reg.size) then + addr <= calc_addr; + else + addr <= (others => '0'); + end if; + + ena <= calc_ena; + + end if; + end process; + + + +end architecture; + + + + + + diff --git a/sim/src/procbus/testprocessor.vhd b/sim/src/procbus/testprocessor.vhd new file mode 100644 index 0000000..f13dc83 --- /dev/null +++ b/sim/src/procbus/testprocessor.vhd @@ -0,0 +1,386 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +library rs232; +library mem; + +library procbus; +use procbus.pProc_bus.all; + +entity eTestprocessor is + generic + ( + clk_speed : integer := 50000000; + baud : integer := 115200; + is_simu : std_logic := '0' + ); + port + ( + clk : in std_logic; + bootloader : in std_logic; + debugaccess : in std_logic; + command_in : in std_logic; + command_out : out std_logic; + + proc_bus : inout proc_bus_type := ((others => 'Z'), (others => 'Z'), (others => 'Z'), 'Z', 'Z', 'Z'); + + fifo_full_error : out std_logic; + timeout_error : buffer std_logic := '0' + ); +end entity; + +architecture arch of eTestprocessor is + + type tState is + ( + START, + BOOTLOADER_READ, + BOOTLOADER_READWAIT, + BOOTLOADER_WRITEWAIT, + BOTLOADER_WRITE_START, + DEBUGIDLE, + RECEIVELENGTH, + BLOCKWRITE, + BLOCKWRITE_WAIT, + BLOCKREAD_READ, + BLOCKREAD_WAIT, + BLOCKREAD_SEND + ); + signal state : tState := START; + + signal startcounter : integer range 0 to 100000001 := 0; + + signal boot_read_addr : unsigned(proc_busadr - 1 downto 0) := to_unsigned(2097152, proc_busadr); -- flash + signal boot_write_addr : unsigned(proc_busadr - 1 downto 0) := to_unsigned(524288, proc_busadr); -- sram + signal boot_cnt : integer range 0 to 65535 := 0; + + signal receive_command : std_logic_vector(31 downto 0); + signal receive_byte_nr : integer range 0 to 4 := 0; + + signal rx_valid : std_logic; + signal rx_byte : std_logic_vector(7 downto 0); + + signal transmit_command : std_logic_vector(31 downto 0); + signal transmit_byte_nr : integer range 0 to 4 := 4; + signal sendbyte : std_logic_vector(7 downto 0) := (others => '0'); + signal tx_enable : std_logic := '0'; + signal tx_busy : std_logic; + + signal proc_din : std_logic_vector(31 downto 0); + signal proc_dout : std_logic_vector(31 downto 0) := (others => '0'); + signal proc_adr : std_logic_vector(proc_busadr - 1 downto 0) := (others => '0'); + signal proc_rnw : std_logic := '0'; + signal proc_ena : std_logic := '0'; + signal proc_done : std_logic; + + signal blocklength_m1 : integer range 0 to 255 := 0; + signal workcount : integer range 0 to 255 := 0; + signal addr_buffer : std_logic_vector(proc_busadr - 1 downto 0) := (others => '0'); + + signal Fifo_writena : std_logic; + signal Fifo_full : std_logic; + signal Fifo_Dout : std_logic_vector(7 downto 0); + signal Fifo_Rd : std_logic := '0'; + signal Fifo_Empty : std_logic; + signal Fifo_valid : std_logic; + + constant TIMEOUTVALUE : integer := 100000000; + signal timeout : integer range 0 to TIMEOUTVALUE := 0; + + +begin + + iProc_bus : entity procbus.eProc_bus + port map + ( + proc_din => proc_din, + proc_dout => proc_dout, + proc_adr => proc_adr, + proc_rnw => proc_rnw, + proc_ena => proc_ena, + proc_done => proc_done, + proc_bus => proc_bus + ); + + iReceiveFifo : entity mem.SyncFifo + generic map + ( + SIZE => 1024, + DATAWIDTH => 8, + NEARFULLDISTANCE => 128 + ) + port map + ( + clk => clk, + reset => timeout_error, + + Din => rx_byte, + Wr => Fifo_writena, + Full => Fifo_full, + NearFull => open, + + Dout => Fifo_Dout, + Rd => Fifo_Rd, + Empty => Fifo_Empty + ); + + Fifo_writena <= rx_valid and (debugaccess or is_simu); + fifo_full_error <= Fifo_full; + + process (clk) + begin + if rising_edge(clk) then + + proc_ena <= '0'; + tx_enable <= '0'; + timeout_error <= '0'; + + Fifo_valid <= Fifo_rd; + Fifo_rd <= '0'; + if (Fifo_Empty = '0' and Fifo_rd = '0' and Fifo_valid = '0' and (state = DEBUGIDLE or state = RECEIVELENGTH or state = BLOCKWRITE)) then + Fifo_rd <= '1'; + end if; + + if (timeout < TIMEOUTVALUE) then + timeout <= timeout + 1; + end if; + + case state is + + when START => + startcounter <= startcounter + 1; + --if ((is_simu = '0' and startcounter = 100000000) or (is_simu = '1' and startcounter = 10000)) then + if ((is_simu = '0' and startcounter = 100000000) or (is_simu = '1' and startcounter = 1)) then + if (bootloader = '1') then + state <= BOOTLOADER_READ; + else + state <= DEBUGIDLE; + end if; + end if; + + when BOOTLOADER_READ => + proc_adr <= std_logic_vector(boot_read_addr); + proc_rnw <= '1'; + proc_ena <= '1'; + state <= BOOTLOADER_READWAIT; + boot_read_addr <= boot_read_addr + 1; + + when BOOTLOADER_READWAIT => + if (proc_done = '1') then + proc_dout <= proc_din; + proc_adr <= std_logic_vector(boot_write_addr); + proc_rnw <= '0'; + proc_ena <= '1'; + state <= BOOTLOADER_WRITEWAIT; + boot_write_addr <= boot_write_addr + 1; + end if; + + when BOOTLOADER_WRITEWAIT => + if (proc_done = '1') then + boot_cnt <= boot_cnt + 1; + if ((is_simu = '0' and boot_cnt = 32767) or (is_simu = '1' and boot_cnt = 127)) then + proc_dout <= x"00080000"; + proc_adr <= std_logic_vector(to_unsigned(1152, proc_busadr)); -- core addr + proc_rnw <= '0'; + proc_ena <= '1'; + state <= BOTLOADER_WRITE_START; + else + state <= BOOTLOADER_READ; + end if; + end if; + + when BOTLOADER_WRITE_START => + if (proc_done = '1') then + proc_dout <= x"00000001"; + proc_adr <= std_logic_vector(to_unsigned(1024, proc_busadr)); -- core enable + proc_rnw <= '0'; + proc_ena <= '1'; + state <= DEBUGIDLE; + end if; + + + -- receive register command + when DEBUGIDLE => + blocklength_m1 <= 0; + workcount <= 0; + timeout <= 0; + + if (Fifo_valid = '1') then + receive_command((receive_byte_nr*8)+7 downto (receive_byte_nr*8)) <= Fifo_Dout; + if (receive_byte_nr < 3) then + receive_byte_nr <= receive_byte_nr + 1; + else + receive_byte_nr <= 0; + addr_buffer <= Fifo_Dout(1 downto 0) & receive_command(23 downto 0); + proc_rnw <= Fifo_Dout(6); + + if (Fifo_Dout(7) = '0') then -- non block mode + if (Fifo_Dout(6) = '1') then + state <= BLOCKREAD_READ; + else + state <= BLOCKWRITE; + end if; + else -- block mode + state <= RECEIVELENGTH; + end if; + end if; + end if; + + when RECEIVELENGTH => + if (timeout = TIMEOUTVALUE) then + state <= DEBUGIDLE; + timeout_error <= '1'; + end if; + + if (Fifo_valid = '1') then + blocklength_m1 <= to_integer(unsigned(Fifo_Dout)); + if (receive_command(30) = '1') then + state <= BLOCKREAD_READ; + else + state <= BLOCKWRITE; + end if; + end if; + + -- write + when BLOCKWRITE => + if (timeout = TIMEOUTVALUE) then + state <= DEBUGIDLE; + timeout_error <= '1'; + end if; + + if (Fifo_valid = '1') then + receive_command((receive_byte_nr*8)+7 downto (receive_byte_nr*8)) <= Fifo_Dout; + if (receive_byte_nr < 3) then + receive_byte_nr <= receive_byte_nr + 1; + else + receive_byte_nr <= 0; + proc_adr <= addr_buffer; + proc_dout <= Fifo_Dout & receive_command(23 downto 0); + proc_ena <= '1'; + addr_buffer <= std_logic_vector(unsigned(addr_buffer) + 1); + state <= BLOCKWRITE_WAIT; + end if; + end if; + + when BLOCKWRITE_WAIT => + if (timeout = TIMEOUTVALUE) then + state <= DEBUGIDLE; + timeout_error <= '1'; + end if; + + if (proc_done = '1') then + if (workcount >= blocklength_m1) then + state <= DEBUGIDLE; + else + workcount <= workcount + 1; + state <= BLOCKWRITE; + end if; + end if; + + -- read + when BLOCKREAD_READ => + proc_adr <= addr_buffer; + proc_ena <= '1'; + addr_buffer <= std_logic_vector(unsigned(addr_buffer) + 1); + state <= BLOCKREAD_WAIT; + + when BLOCKREAD_WAIT => + if (timeout = TIMEOUTVALUE) then + state <= DEBUGIDLE; + timeout_error <= '1'; + end if; + + if (proc_done = '1') then + transmit_command <= proc_din; + transmit_byte_nr <= 0; + state <= BLOCKREAD_SEND; + end if; + + when BLOCKREAD_SEND => + if (timeout = TIMEOUTVALUE) then + state <= DEBUGIDLE; + timeout_error <= '1'; + end if; + + if (tx_busy = '0') then + transmit_byte_nr <= transmit_byte_nr + 1; + sendbyte <= transmit_command((transmit_byte_nr*8)+7 downto (transmit_byte_nr*8)); + tx_enable <= '1'; + if (transmit_byte_nr = 3) then + if (workcount >= blocklength_m1) then + state <= DEBUGIDLE; + else + workcount <= workcount + 1; + state <= BLOCKREAD_READ; + end if; + end if; + end if; + + end case; + + end if; + end process; + + gtestbench : if (is_simu = '1') generate + begin + + itbrs232_receiver : entity rs232.tbrs232_receiver + port map + ( + clk => clk, + rx_byte => rx_byte, + valid => rx_valid, + rx => command_in + ); + + itbrs232_transmitter : entity rs232.tbrs232_transmitter + port map + ( + clk => clk, + busy => tx_busy, + sendbyte => sendbyte, + enable => tx_enable, + tx => command_out + ); + + + end generate; + + gsynthesis : if (is_simu = '0') generate + begin + + irs232_receiver : entity rs232.rs232_receiver + generic map + ( + clk_speed => clk_speed, + baud => baud + ) + port map + ( + clk => clk, + rx_byte => rx_byte, + valid => rx_valid, + rx => command_in + ); + + irs232_transmitter : entity rs232.rs232_transmitter + generic map + ( + clk_speed => clk_speed, + baud => baud + ) + port map + ( + clk => clk, + busy => tx_busy, + sendbyte => sendbyte, + enable => tx_enable, + tx => command_out + ); + + end generate; + + +end architecture; + diff --git a/sim/src/reg_map/reg_gameboy.vhd b/sim/src/reg_map/reg_gameboy.vhd new file mode 100644 index 0000000..6fc10bb --- /dev/null +++ b/sim/src/reg_map/reg_gameboy.vhd @@ -0,0 +1,70 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +library procbus; +use procbus.pProc_bus.all; +use procbus.pRegmap.all; + +package pReg_gameboy is + + -- range 1048576 .. 2097151 + -- adr upper lower size default accesstype) + constant Reg_GB_on : regmap_type := (1056768, 0, 0, 1, 0, readwrite); -- on = 1 + + constant Reg_GB_lockspeed : regmap_type := (1056769, 0, 0, 1, 0, readwrite); -- 1 = 100% speed + + constant Reg_GB_flash_1m : regmap_type := (1056770, 0, 0, 1, 0, readwrite); + + constant Reg_GB_CyclePrecalc : regmap_type := (1056771, 15, 0, 1, 100, readwrite); + constant Reg_GB_CyclesMissing : regmap_type := (1056772, 31, 0, 1, 0, readonly); + + constant Reg_GB_BusAddr : regmap_type := (1056773, 27, 0, 1, 0, readwrite); + constant Reg_GB_BusRnW : regmap_type := (1056773, 28, 28, 1, 0, readwrite); + constant Reg_GB_BusACC : regmap_type := (1056773, 30, 29, 1, 0, readwrite); + constant Reg_GB_BusWriteData : regmap_type := (1056774, 31, 0, 1, 0, readwrite); + constant Reg_GB_BusReadData : regmap_type := (1056775, 31, 0, 1, 0, readonly); + + constant Reg_GB_MaxPakAddr : regmap_type := (1056776, 24, 0, 1, 0, readwrite); + + constant Reg_GB_VsyncSpeed : regmap_type := (1056777, 31, 0, 1, 0, readwrite); + + -- joypad + constant Reg_GB_KeyUp : regmap_type := (1056778, 0, 0, 1, 0, readwrite); + constant Reg_GB_KeyDown : regmap_type := (1056778, 1, 1, 1, 0, readwrite); + constant Reg_GB_KeyLeft : regmap_type := (1056778, 2, 2, 1, 0, readwrite); + constant Reg_GB_KeyRight : regmap_type := (1056778, 3, 3, 1, 0, readwrite); + constant Reg_GB_KeyA : regmap_type := (1056778, 4, 4, 1, 0, readwrite); + constant Reg_GB_KeyB : regmap_type := (1056778, 5, 5, 1, 0, readwrite); + constant Reg_GB_KeyL : regmap_type := (1056778, 6, 6, 1, 0, readwrite); + constant Reg_GB_KeyR : regmap_type := (1056778, 7, 7, 1, 0, readwrite); + constant Reg_GB_KeyStart : regmap_type := (1056778, 8, 8, 1, 0, readwrite); + constant Reg_GB_KeySelect : regmap_type := (1056778, 9, 9, 1, 0, readwrite); + + -- special settngs + constant Reg_GB_cputurbo : regmap_type := (1056780, 0, 0, 1, 0, readwrite); -- 1 = cpu free running, all other 16 mhz + constant Reg_GB_SramFlashEna : regmap_type := (1056781, 0, 0, 1, 0, readwrite); -- 1 = enabled, 0 = disable (disable for copy protection in some games) + constant Reg_GB_MemoryRemap : regmap_type := (1056782, 0, 0, 1, 0, readwrite); -- 1 = enabled, 0 = disable (enable for copy protection in some games) + constant Reg_GB_SaveState : regmap_type := (1056783, 0, 0, 1, 0, Pulse); + constant Reg_GB_LoadState : regmap_type := (1056784, 0, 0, 1, 0, Pulse); + constant Reg_GB_FrameBlend : regmap_type := (1056785, 0, 0, 1, 0, readwrite); -- mix last and current frame + constant Reg_GB_Pixelshade : regmap_type := (1056786, 2, 0, 1, 0, readwrite); -- pixel shade 1..4, 0 = off + constant Reg_GB_SaveStateAddr : regmap_type := (1056787, 25, 0, 1, 0, readwrite); -- address to save/load savestate + constant Reg_GB_Rewind_on : regmap_type := (1056788, 0, 0, 1, 0, readwrite); + constant Reg_GB_Rewind_active : regmap_type := (1056789, 0, 0, 1, 0, readwrite); + + --debug + constant Reg_GB_DEBUG_CPU_PC : regmap_type := (1056800, 31, 0, 1, 0, readonly); + constant Reg_GB_DEBUG_CPU_MIX : regmap_type := (1056801, 31, 0, 1, 0, readonly); + constant Reg_GB_DEBUG_IRQ : regmap_type := (1056802, 31, 0, 1, 0, readonly); + constant Reg_GB_DEBUG_DMA : regmap_type := (1056803, 31, 0, 1, 0, readonly); + constant Reg_GB_DEBUG_MEM : regmap_type := (1056804, 31, 0, 1, 0, readonly); + + --cheats + constant Reg_GB_CHEAT_FLAGS : regmap_type := (1056810, 31, 0, 1, 0, readwrite); + constant Reg_GB_CHEAT_ADDRESS : regmap_type := (1056811, 31, 0, 1, 0, readwrite); + constant Reg_GB_CHEAT_COMPARE : regmap_type := (1056812, 31, 0, 1, 0, readwrite); + constant Reg_GB_CHEAT_REPLACE : regmap_type := (1056813, 31, 0, 1, 0, readwrite); + constant Reg_GB_CHEAT_RESET : regmap_type := (1056814, 0, 0, 1, 0, Pulse); + +end package; diff --git a/sim/src/rs232/rs232_receiver.vhd b/sim/src/rs232/rs232_receiver.vhd new file mode 100644 index 0000000..1e60821 --- /dev/null +++ b/sim/src/rs232/rs232_receiver.vhd @@ -0,0 +1,79 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + + +entity rs232_receiver is + generic + ( + clk_speed : integer := 50000000; + baud : integer := 115200 + ); + port + ( + clk : in std_logic; + rx_byte : out std_logic_vector(7 downto 0); + valid : out std_logic; + rx : in std_logic + ); +end entity; + +architecture arch of rs232_receiver is + + constant bittime : integer := (clk_speed / baud)-1; + + signal idle : std_logic := '1'; + + signal rx_ff : std_logic := '0'; + signal old_rx : std_logic := '0'; + + signal bitslow : integer range 0 to bittime+1 := 0; + signal bitcount : integer range 0 to 8 := 0; + + signal byte_rx : std_logic_vector(8 downto 0) := (others => '0'); + signal valid_buffer : std_logic := '0'; + + +begin + + + process (clk) + begin + if rising_edge(clk) then + + valid_buffer <= '0'; + + rx_ff <= rx; + old_rx <= rx_ff; + + if (idle = '1' and rx_ff = '0' and old_rx = '1') then + bitslow <= (bittime / 2) + 1; + bitcount <= 0; + idle <= '0'; + end if; + + if (idle = '0') then + bitslow <= bitslow + 1; + if (bitslow = bittime) then + bitslow <= 0; + byte_rx(bitcount) <= rx_ff; + if (bitcount < 8) then + bitcount <= bitcount + 1; + else + bitcount <= 0; + idle <= '1'; + valid_buffer <= '1'; + end if; + end if; + end if; + + + end if; + end process; + + rx_byte <= byte_rx(8 downto 1); + + valid <= valid_buffer; + + +end architecture; \ No newline at end of file diff --git a/sim/src/rs232/rs232_transmitter.vhd b/sim/src/rs232/rs232_transmitter.vhd new file mode 100644 index 0000000..b0f043d --- /dev/null +++ b/sim/src/rs232/rs232_transmitter.vhd @@ -0,0 +1,69 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + + +entity rs232_transmitter is + generic + ( + clk_speed : integer := 50000000; + baud : integer := 115200 + ); + port + ( + clk : in std_logic; + busy : out std_logic := '0'; + sendbyte : in std_logic_vector(7 downto 0); + enable : in std_logic; + tx : out std_logic + ); +end entity; + +architecture arch of rs232_transmitter is + + constant bittime : integer := (clk_speed / baud)-1; + + signal running : std_logic := '0'; + + signal bitslow : integer range 0 to bittime + 1 := 0; + signal bitcount : integer range 0 to 9 := 0; + + signal byte_tx : std_logic_vector(9 downto 0) := (others => '0'); + + + +begin + + process (clk) + begin + if rising_edge(clk) then + + if (running = '0' and enable = '1') then + running <= '1'; + bitcount <= 0; + bitslow <= 0; + byte_tx <= '1' & sendbyte & '0'; + end if; + + if (running = '1') then + bitslow <= bitslow + 1; + if (bitslow = bittime) then + bitslow <= 0; + if (bitcount < 9) then + bitcount <= bitcount + 1; + else + bitcount <= 0; + running <= '0'; + end if; + end if; + + end if; + + end if; + end process; + + busy <= '1' when running = '1' or enable = '1' else '0'; + + tx <= '1' when running = '0' else byte_tx(bitcount); + +end architecture; \ No newline at end of file diff --git a/sim/src/rs232/tbrs232_receiver.vhd b/sim/src/rs232/tbrs232_receiver.vhd new file mode 100644 index 0000000..fc39e07 --- /dev/null +++ b/sim/src/rs232/tbrs232_receiver.vhd @@ -0,0 +1,40 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + + +entity tbrs232_receiver is + port + ( + clk : in std_logic; + rx_byte : out std_logic_vector(7 downto 0) := (others => '0'); + valid : out std_logic := '0'; + rx : in std_logic + ); +end entity; + +architecture arch of tbrs232_receiver is + + +begin + + process + begin + valid <= '0'; + + wait until rx = '0'; + + wait for 3 ps; + for i in 0 to 7 loop + rx_byte(i) <= rx; + wait for 2 ps; + end loop; + + valid <= '1'; + wait until clk = '0'; + wait until clk = '1'; + valid <= '0'; + + end process; + +end architecture; \ No newline at end of file diff --git a/sim/src/rs232/tbrs232_transmitter.vhd b/sim/src/rs232/tbrs232_transmitter.vhd new file mode 100644 index 0000000..ae238d6 --- /dev/null +++ b/sim/src/rs232/tbrs232_transmitter.vhd @@ -0,0 +1,46 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + + +entity tbrs232_transmitter is + port + ( + clk : in std_logic; + busy : out std_logic := '0'; + sendbyte : in std_logic_vector(7 downto 0); + enable : in std_logic; + tx : out std_logic := '1' + ); +end entity; + +architecture arch of tbrs232_transmitter is + + +begin + + busy <= enable; + + process + begin + tx <= '1'; + + if (enable = '1') then + + tx <= '0'; + wait for 2 ps; + for i in 0 to 7 loop + tx <= sendbyte(i); + wait for 2 ps; + end loop; + tx <= '1'; + + end if; + + wait until clk = '0'; + wait until clk = '1'; + + end process; + + +end architecture; \ No newline at end of file diff --git a/sim/src/tb/framebuffer.vhd b/sim/src/tb/framebuffer.vhd new file mode 100644 index 0000000..c1cecd2 --- /dev/null +++ b/sim/src/tb/framebuffer.vhd @@ -0,0 +1,108 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use STD.textio.all; + +entity framebuffer is + generic + ( + FRAMESIZE_X : integer; + FRAMESIZE_Y : integer + ); + port + ( + clk : in std_logic; + + pixel_in_x : in integer range 0 to (FRAMESIZE_X - 1); + pixel_in_y : in integer range 0 to (FRAMESIZE_Y - 1); + pixel_in_data : in std_logic_vector(14 downto 0); + pixel_in_we : in std_logic + ); +end entity; + +architecture arch of framebuffer is + + -- data write + signal pixel_in_addr : integer range 0 to (FRAMESIZE_X * FRAMESIZE_Y) - 1; + signal pixel_in_data_1 : std_logic_vector(14 downto 0); + signal pixel_in_we_1 : std_logic; + + type tPixelArray is array(0 to (FRAMESIZE_X * FRAMESIZE_Y) - 1) of std_logic_vector(14 downto 0); + signal PixelArray : tPixelArray := (others => (others => '0')); + +begin + + -- fill framebuffer + process (clk) + begin + if rising_edge(clk) then + + pixel_in_addr <= pixel_in_x + (pixel_in_y * 160); + pixel_in_data_1 <= pixel_in_data; + pixel_in_we_1 <= pixel_in_we; + + if (pixel_in_we_1 = '1') then + PixelArray(pixel_in_addr) <= pixel_in_data_1; + end if; + + end if; + end process; + +-- synthesis translate_off + + goutput : if 1 = 1 generate + begin + + process + + file outfile: text; + variable f_status: FILE_OPEN_STATUS; + variable line_out : line; + variable color : unsigned(31 downto 0); + variable linecounter_int : integer; + + begin + + file_open(f_status, outfile, "gra_fb_out.gra", write_mode); + file_close(outfile); + + file_open(f_status, outfile, "gra_fb_out.gra", append_mode); + write(line_out, string'("160#144")); + writeline(outfile, line_out); + + while (true) loop + wait until ((pixel_in_x mod 160) = (160 - 1)) and pixel_in_we = '1'; + linecounter_int := pixel_in_y; + + wait for 100 ns; + + for x in 0 to 159 loop + color := (31 downto 15 => '0') & unsigned(PixelArray(x + linecounter_int * 160)); + color := x"00" & unsigned(color(14 downto 10)) & "000" & unsigned(color(9 downto 5)) & "000" & unsigned(color(4 downto 0)) & "000"; + + write(line_out, to_integer(color)); + write(line_out, string'("#")); + write(line_out, x); + write(line_out, string'("#")); + write(line_out, linecounter_int); + writeline(outfile, line_out); + + end loop; + + file_close(outfile); + file_open(f_status, outfile, "gra_fb_out.gra", append_mode); + + end loop; + + end process; + + end generate goutput; + +-- synthesis translate_on + +end architecture; + + + + + diff --git a/sim/src/tb/gb_bios.vhd b/sim/src/tb/gb_bios.vhd new file mode 100644 index 0000000..d6823f3 --- /dev/null +++ b/sim/src/tb/gb_bios.vhd @@ -0,0 +1,285 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +entity gb_bios is + port + ( + clk : in std_logic; + address : in std_logic_vector(7 downto 0); + data : out std_logic_vector(7 downto 0) + ); +end entity; + +architecture arch of gb_bios is + + type t_rom is array(0 to 255) of std_logic_vector(7 downto 0); + signal rom : t_rom := ( + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00", + x"00" + ); + +begin + + process (clk) + begin + if rising_edge(clk) then + data <= rom(to_integer(unsigned(address))); + end if; + end process; + +end architecture; diff --git a/sim/src/tb/globals.vhd b/sim/src/tb/globals.vhd new file mode 100644 index 0000000..e63127c --- /dev/null +++ b/sim/src/tb/globals.vhd @@ -0,0 +1,13 @@ +library ieee; +use ieee.std_logic_1164.all; + +package globals is + + signal COMMAND_FILE_ENDIAN : std_logic; + signal COMMAND_FILE_NAME : string(1 to 1024); + signal COMMAND_FILE_NAMELEN : integer; + signal COMMAND_FILE_TARGET : integer; + signal COMMAND_FILE_START : std_logic; + signal COMMAND_FILE_ACK : std_logic; + +end package globals; \ No newline at end of file diff --git a/sim/src/tb/sdram_model.vhd b/sim/src/tb/sdram_model.vhd new file mode 100644 index 0000000..154ca67 --- /dev/null +++ b/sim/src/tb/sdram_model.vhd @@ -0,0 +1,113 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +library tb; +use tb.globals.all; + +entity sdram_model is + port + ( + clk : in std_logic; + cart_addr : in std_logic_vector(15 downto 0); + cart_rd : in std_logic; + cart_do : out std_logic_vector(7 downto 0) + ); +end entity; + +architecture arch of sdram_model is + + -- not full size, because of memory required + type t_data is array(0 to (2**23)-1) of integer; + type bit_vector_file is file of bit_vector; + +begin + + process + + variable data : t_data := (others => 0); + + file infile : bit_vector_file; + variable f_status : FILE_OPEN_STATUS; + variable read_byte : std_logic_vector(7 downto 0); + variable next_vector : bit_vector (0 downto 0); + variable actual_len : natural; + variable targetpos : integer; + + -- copy from std_logic_arith, not used here because numeric std is also included + function CONV_STD_LOGIC_VECTOR(ARG: INTEGER; SIZE: INTEGER) return STD_LOGIC_VECTOR is + variable result: STD_LOGIC_VECTOR (SIZE-1 downto 0); + variable temp: integer; + begin + + temp := ARG; + for i in 0 to SIZE-1 loop + + if (temp mod 2) = 1 then + result(i) := '1'; + else + result(i) := '0'; + end if; + + if temp > 0 then + temp := temp / 2; + elsif (temp > integer'low) then + temp := (temp - 1) / 2; -- simulate ASR + else + temp := temp / 2; -- simulate ASR + end if; + end loop; + + return result; + end; + + begin + wait until rising_edge(clk); + + if (cart_rd = '1') then + cart_do <= x"FF"; + wait until rising_edge(clk); + wait until rising_edge(clk); + wait until rising_edge(clk); + wait until rising_edge(clk); + wait until rising_edge(clk); + wait until rising_edge(clk); + cart_do <= std_logic_vector(to_unsigned(data(to_integer(unsigned(cart_addr))), 8)); + end if; + + COMMAND_FILE_ACK <= '0'; + if COMMAND_FILE_START = '1' then + + assert false report "received" severity note; + assert false report COMMAND_FILE_NAME(1 to COMMAND_FILE_NAMELEN) severity note; + + file_open(f_status, infile, COMMAND_FILE_NAME(1 to COMMAND_FILE_NAMELEN), read_mode); + + targetpos := COMMAND_FILE_TARGET; + + while (not endfile(infile)) loop + + read(infile, next_vector, actual_len); + + read_byte := CONV_STD_LOGIC_VECTOR(bit'pos(next_vector(0)), 8); + + --report "read_byte=" & integer'image(to_integer(unsigned(read_byte))); + + data(targetpos) := to_integer(unsigned(read_byte)); + targetpos := targetpos + 1; + + end loop; + + file_close(infile); + + COMMAND_FILE_ACK <= '1'; + + end if; + + + + end process; + +end architecture; + + diff --git a/sim/src/tb/stringprocessor.vhd b/sim/src/tb/stringprocessor.vhd new file mode 100644 index 0000000..90d4dca --- /dev/null +++ b/sim/src/tb/stringprocessor.vhd @@ -0,0 +1,389 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use STD.textio.all; + +library procbus; +use procbus.pProc_bus.all; + +use work.globals.all; + +entity estringprocessor is + generic + ( + clk_speed : integer := 50000000 + ); + port + ( + ready : in std_logic; + tx_command : out std_logic_vector(31 downto 0); + tx_bytes : out integer range 0 to 4; + tx_enable : out std_logic := '0'; + rx_command : in std_logic_vector(31 downto 0); + rx_valid : in std_logic + ); +end entity; + +architecture arch of estringprocessor is + + constant clk_period : time := (1000000000 / clk_speed) * 1 ns; + + signal string_command : string(1 to 3); + + type t_internal_variables is array(0 to 256) of integer; + signal internal_variables : t_internal_variables := (others => 0); + + type r_thread_record is record + id : integer; + waittime : integer; + waitcommand : line; + end record; + + type t_thread_buffers is array(0 to 255) of r_thread_record; + + + procedure find_id + ( + variable t_buf : inout t_thread_buffers; + variable id : in integer; + variable slot : out integer + ) is + begin + for i in 0 to 255 loop + if (t_buf(i).id = id) then + slot := i; + exit; + elsif (t_buf(i).id = -1) then + t_buf(i).id := id; + slot := i; + exit; + end if; + end loop; + end procedure; + + + +begin + + + process + + file infile : text; + file outfile : text; + variable f_status : FILE_OPEN_STATUS; + variable newdat : LINE; + variable endfound : boolean; + variable buf : LINE; + variable run : boolean := true; + + variable line_out : line; + variable debugline_out : line; + variable command : string(1 to 3); + + variable dev_null_str3 : string (1 to 3); + variable dev_null_str6 : string (1 to 6); + variable OK : boolean := FALSE; + variable para_int1 : integer; + variable para_int2 : integer; + variable para_int3 : integer; + variable para_int4 : integer; + + variable address : integer; + variable data : integer; + variable count : integer; + + variable thread_buffers : t_thread_buffers := (others => (-1, 0, null)); + + variable freerun : boolean := false; + + begin + + file_open(f_status, outfile, "input.txt", write_mode); + file_close(outfile); + file_open(f_status, outfile, "output.txt", write_mode); + file_close(outfile); + + file_open(f_status, infile, "input.txt", read_mode); + + wait for 1000 ns; + + while (run) loop + + if not endfile(infile) then + + endfound := false; + buf := null; + while (endfound = false) loop + if not endfile(infile) then + readline(infile,newdat); + for i in 1 to newdat'length loop + write(buf, newdat(i to i)); + if (newdat(i to i) = "&") then + endfound := true; + end if; + end loop; + end if; + end loop; + + command := buf(1 to 3); + string_command <= command; + + line_out := null; + buf(buf'length) := ' '; + write(line_out, buf(1 to buf'length)); + write(line_out, string'("# ")); + + if (command(1 to 3) = String'("set")) then + -- command + Read(buf, dev_null_str6); + -- command nr + Read(buf, para_int1, OK); + Read(buf, dev_null_str3); + -- process id + Read(buf, para_int1, OK); + Read(buf, dev_null_str3); + -- target for reading + Read(buf, para_int2, OK); + Read(buf, dev_null_str3); + -- count + Read(buf, count, OK); + Read(buf, dev_null_str3); + -- address in target space + Read(buf, address, OK); + Read(buf, dev_null_str3); + + -- write address + if (ready = '0') then + wait until ready = '1'; + end if; + tx_command <= "000000" & std_logic_vector(to_unsigned(address,proc_busadr)); + if (count > 1) then + tx_command(31) <= '1'; + end if; + tx_bytes <= 4; + tx_enable <= '1'; + wait for clk_period*2; + tx_enable <= '0'; + + -- write block size + if (count > 1) then + if (ready = '0') then + wait until ready = '1'; + end if; + tx_command <= x"000000" & std_logic_vector(to_unsigned(count - 1,8)); + tx_bytes <= 1; + tx_enable <= '1'; + wait for clk_period*2; + tx_enable <= '0'; + end if; + + for i in 1 to count loop + + -- value to write + Read(buf, data, OK); + if (i < count) then + Read(buf, dev_null_str3); + end if; + + -- write data + if (ready = '0') then + wait until ready = '1'; + end if; + tx_command <= std_logic_vector(to_signed(data,proc_buswidth)); + tx_bytes <= 4; + tx_enable <= '1'; + wait for clk_period*2; + tx_enable <= '0'; + wait for 20*clk_period; -- this is required, because there is no answer for write commands + + address := address + 1; + + end loop; + + write(line_out, string'("&")); + file_open(f_status, outfile, "output.txt", append_mode); + writeline(outfile, line_out); + file_close(outfile); + end if; + + if (command(1 to 3) = String'("get")) then + -- command + Read(buf, dev_null_str6); + -- command nr + Read(buf, para_int1, OK); + Read(buf, dev_null_str3); + -- process id + Read(buf, para_int1, OK); + Read(buf, dev_null_str3); + -- target for reading + Read(buf, para_int1, OK); + Read(buf, dev_null_str3); + -- count + Read(buf, count, OK); + Read(buf, dev_null_str3); + -- address in target space + Read(buf, address, OK); + + -- write address + if (ready = '0') then + wait until ready = '1'; + end if; + tx_command <= "010000" & std_logic_vector(to_unsigned(address,proc_busadr)); + if (count > 1) then + tx_command(31) <= '1'; + end if; + tx_bytes <= 4; + tx_enable <= '1'; + wait for clk_period*2; + tx_enable <= '0'; + + -- write block size + if (count > 1) then + if (ready = '0') then + wait until ready = '1'; + end if; + tx_command <= x"000000" & std_logic_vector(to_unsigned(count - 1,8)); + tx_bytes <= 1; + tx_enable <= '1'; + wait for clk_period*2; + tx_enable <= '0'; + end if; + + for i in 1 to count loop + + if (rx_valid = '0') then + wait until rx_valid = '1'; + end if; + data := to_integer(signed(rx_command(31 downto 0))); + wait until rx_valid = '0'; + + write(line_out, data); + write(line_out, string'("#")); + + address := address + 1; + + end loop; + + write(line_out, string'("&")); + file_open(f_status, outfile, "output.txt", append_mode); + writeline(outfile, line_out); + file_close(outfile); + end if; + + if (command(1 to 3) = String'("fil")) then + -- command + Read(buf, dev_null_str6); + -- command nr + Read(buf, para_int1, OK); + Read(buf, dev_null_str3); + -- process id + Read(buf, para_int1, OK); + Read(buf, dev_null_str3); + -- endianess + Read(buf, para_int2, OK); + Read(buf, dev_null_str3); + -- address in target space + Read(buf, address, OK); + Read(buf, dev_null_str3); + + COMMAND_FILE_NAME <= (others => ' '); + COMMAND_FILE_NAME(1 to buf'length) <= buf(1 to buf'length); + COMMAND_FILE_NAMELEN <= buf'length; + COMMAND_FILE_TARGET <= address; + if (para_int2 = 1) then + COMMAND_FILE_ENDIAN <= '1'; + else + COMMAND_FILE_ENDIAN <= '0'; + end if; + COMMAND_FILE_START <= '1'; + wait until COMMAND_FILE_ACK = '1'; + COMMAND_FILE_START <= '0'; + wait for 20 ns; + + write(line_out, string'("&")); + file_open(f_status, outfile, "output.txt", append_mode); + writeline(outfile, line_out); + file_close(outfile); + end if; + + if (command(1 to 3) = String'("brk")) then + run := false; + end if; + + if (command(1 to 3) = String'("wtn")) then + -- command + Read(buf, dev_null_str6); + -- command nr + Read(buf, para_int1, OK); + Read(buf, dev_null_str3); + -- process id + Read(buf, para_int1, OK); + Read(buf, dev_null_str3); + -- time to wait + Read(buf, para_int2, OK); + + + write(line_out, string'("&")); + if (para_int2 = -1) then + freerun := true; + find_id(thread_buffers, para_int1, para_int3); + thread_buffers(para_int3).waittime := 2; + thread_buffers(para_int3).waitcommand := line_out; + else + find_id(thread_buffers, para_int1, para_int3); + thread_buffers(para_int3).waittime := para_int2; + thread_buffers(para_int3).waitcommand := line_out; + end if; + + end if; + + wait for 1 ps; + + end if; + + OK := false; + for i in 0 to 255 loop + if (thread_buffers(i).id /= -1) then + if (thread_buffers(i).waittime = 1) then + file_open(f_status, outfile, "output.txt", append_mode); + writeline(outfile, thread_buffers(i).waitcommand); + file_close(outfile); + end if; + if (thread_buffers(i).waittime > 0) then + OK := true; + thread_buffers(i).waittime := thread_buffers(i).waittime - 1; + end if; + end if; + end loop; + + if (OK = true or freerun = true) then + wait for 1 ns; + end if; + + end loop; + + file_close(infile); + + wait; + + end process; + + + + +end architecture; + + + + + + + + + + + + + + + diff --git a/sim/src/tb/tb.vhd b/sim/src/tb/tb.vhd new file mode 100644 index 0000000..b0160be --- /dev/null +++ b/sim/src/tb/tb.vhd @@ -0,0 +1,263 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +library tb; +library gameboy; + +library procbus; +use procbus.pProc_bus.all; +use procbus.pRegmap.all; + +library reg_map; +use reg_map.pReg_gameboy.all; + +entity etb is +end entity; + +architecture arch of etb is + + constant clk_speed : integer := 100000000; + constant baud : integer := 25000000; + + signal clk100 : std_logic := '1'; + + signal reset : std_logic := '1'; + signal clksys : std_logic := '1'; + + signal clkdiv : unsigned(2 downto 0) := (others => '0'); + signal nextdiv : unsigned(2 downto 0) := (others => '0'); + + signal ce : std_logic := '1'; + signal ce_2x : std_logic := '1'; + + signal command_in : std_logic; + signal command_out : std_logic; + signal command_out_filter : std_logic; + + signal proc_bus_in : proc_bus_type; + + signal lcd_clkena : std_logic; + signal lcd_clkena_1 : std_logic := '0'; + signal lcd_data : std_logic_vector(14 downto 0); + signal lcd_mode : std_logic_vector(1 downto 0); + signal lcd_on : std_logic; + signal lcd_vsync : std_logic; + signal lcd_vsync_1 : std_logic := '0'; + + signal gbc_bios_addr : std_logic_vector(11 downto 0); + signal gbc_bios_do : std_logic_vector(7 downto 0); + + signal cart_addr : std_logic_vector(15 downto 0); + signal cart_rd : std_logic; + signal cart_wr : std_logic; + signal cart_act : std_logic; + signal cart_do : std_logic_vector(7 downto 0); + signal cart_di : std_logic_vector(7 downto 0); + + + signal pixel_out_x : integer range 0 to 159; + signal pixel_out_y : integer range 0 to 143; + signal pixel_out_data : std_logic_vector(14 downto 0); + signal pixel_out_we : std_logic := '0'; + + + -- settings + signal GB_on : std_logic_vector(Reg_GB_on.upper downto Reg_GB_on.lower) := (others => '0'); + + +begin + + reset <= not GB_on(0); + clksys <= not clksys after 15 ns; + + clk100 <= not clk100 after 5 ns; + + -- registers + iReg_GBA_on : entity procbus.eProcReg generic map (Reg_GB_on ) port map (clk100, proc_bus_in, GB_on, GB_on); + + cart_act <= cart_rd or cart_wr; + + ispeedcontrol : entity gameboy.speedcontrol + port map + ( + clk_sys => clksys, + pause => '0', + speedup => '1', + cart_act => cart_act, + ce => ce, + ce_2x => ce_2x + ); + + + isdram_model : entity tb.sdram_model + port map + ( + clk => clksys, + cart_addr => cart_addr, + cart_rd => cart_rd, + cart_do => cart_do + ); + + --itetris : entity gameboy.tetris + --port map + --( + -- clk => clksys, + -- address => cart_addr(14 downto 0), + -- data => cart_do + --); + + igb : entity gameboy.gb + port map + ( + reset => reset, + + clk_sys => clksys, + ce => ce, + ce_2x => ce_2x, + --ce_sound => ce_2x, + + fast_boot => '1', + joystick => x"00", + isGBC => '0', + isGBC_game => '0', + + -- cartridge interface + -- can adress up to 1MB ROM + cart_addr => cart_addr, + cart_rd => cart_rd, + cart_wr => cart_wr, + cart_do => cart_do, + cart_di => cart_di, + + --gbc bios interface + gbc_bios_addr => gbc_bios_addr, + gbc_bios_do => gbc_bios_do, + + -- audio + audio_l => open, + audio_r => open, + + -- lcd interface + lcd_clkena => lcd_clkena, + lcd_data => lcd_data, + lcd_mode => lcd_mode, + lcd_on => lcd_on, + lcd_vsync => lcd_vsync, + + joy_p54 => open, + joy_din => "0000", + + speed => open, --GBC + + gg_reset => reset, + gg_en => '0', + gg_code => (128 downto 0 => '0'), + gg_available => open, + + --serial port + sc_int_clock2 => open, + serial_clk_in => '0', + serial_clk_out => open, + serial_data_in => '0', + serial_data_out => open + ); + + igb_bios : entity tb.gb_bios + port map + ( + clk => clksys, + address => gbc_bios_addr(7 downto 0), + data => gbc_bios_do + ); + + process(clksys) + begin + if rising_edge(clksys) then + pixel_out_we <= '0'; + lcd_vsync_1 <= lcd_vsync; + lcd_clkena_1 <= lcd_clkena; + if (lcd_on = '1') then + if (lcd_vsync = '1' and lcd_vsync_1 = '0') then + pixel_out_x <= 0; + pixel_out_y <= 0; + elsif (lcd_clkena_1 = '1' and lcd_clkena = '0') then + pixel_out_x <= 0; + pixel_out_we <= '1'; + if (pixel_out_y < 143) then + pixel_out_y <= pixel_out_y + 1; + end if; + elsif (lcd_clkena = '1' and ce = '1') then + if (pixel_out_x < 159) then + pixel_out_x <= pixel_out_x + 1; + end if; + pixel_out_we <= '1'; + end if; + end if; + + case (lcd_data(1 downto 0)) is + when "00" => pixel_out_data <= "11111" & "11111" & "11111"; + when "01" => pixel_out_data <= "10000" & "10000" & "10000"; + when "10" => pixel_out_data <= "01000" & "01000" & "01000"; + when "11" => pixel_out_data <= "00000" & "00000" & "00000"; + when others => pixel_out_data <= "00000" & "00000" & "11111"; + end case; + + end if; + end process; + + iframebuffer : entity work.framebuffer + generic map + ( + FRAMESIZE_X => 160, + FRAMESIZE_Y => 144 + ) + port map + ( + clk => clksys, + + pixel_in_x => pixel_out_x, + pixel_in_y => pixel_out_y, + pixel_in_data => pixel_out_data, + pixel_in_we => pixel_out_we + ); + + iTestprocessor : entity procbus.eTestprocessor + generic map + ( + clk_speed => clk_speed, + baud => baud, + is_simu => '1' + ) + port map + ( + clk => clk100, + bootloader => '0', + debugaccess => '1', + command_in => command_in, + command_out => command_out, + + proc_bus => proc_bus_in, + + fifo_full_error => open, + timeout_error => open + ); + + command_out_filter <= '0' when command_out = 'Z' else command_out; + + itb_interpreter : entity tb.etb_interpreter + generic map + ( + clk_speed => clk_speed, + baud => baud + ) + port map + ( + clk => clk100, + command_in => command_in, + command_out => command_out_filter + ); + +end architecture; + + diff --git a/sim/src/tb/tb_interpreter.vhd b/sim/src/tb/tb_interpreter.vhd new file mode 100644 index 0000000..094591e --- /dev/null +++ b/sim/src/tb/tb_interpreter.vhd @@ -0,0 +1,125 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +library rs232; +library tb; + +entity etb_interpreter is + generic + ( + clk_speed : integer := 50000000; + baud : integer := 115200 + ); + port + ( + clk : in std_logic; + command_in : out std_logic; + command_out : in std_logic + ); +end entity; + +architecture arch of etb_interpreter is + + signal slow_counter : unsigned(3 downto 0) := (others => '0'); + + signal addr_counter : integer := 0; + + signal idle : std_logic := '1'; + + signal transmit_command : std_logic_vector(31 downto 0) := (others => '0'); + signal transmit_byte_nr : integer := 0; + signal sendbyte : std_logic_vector(7 downto 0) := (others => '0'); + signal tx_enable : std_logic := '0'; + signal tx_busy : std_logic; + + signal receive_command : std_logic_vector(31 downto 0) := (others => '0'); + signal receive_byte_nr : integer := 0; + signal receive_valid : std_logic := '0'; + + signal rx_valid : std_logic; + signal rx_byte : std_logic_vector(7 downto 0); + + signal proc_command : std_logic_vector(31 downto 0); + signal proc_bytes : integer range 0 to 4; + signal proc_enable : std_logic; + +begin + + process (clk) + begin + if rising_edge(clk) then + + -- tx side + tx_enable <= '0'; + + if (idle = '1' and proc_enable = '1') then + addr_counter <= addr_counter + 1; + transmit_command <= proc_command; + transmit_byte_nr <= 0; + idle <= '0'; + elsif (idle = '0') then + if (tx_busy = '0') then + transmit_byte_nr <= transmit_byte_nr + 1; + sendbyte <= transmit_command((transmit_byte_nr*8)+7 downto (transmit_byte_nr*8)); + tx_enable <= '1'; + if (transmit_byte_nr = (proc_bytes - 1)) then + idle <= '1'; + end if; + end if; + end if; + + -- rx side + receive_valid <= '0'; + if (rx_valid = '1') then + receive_byte_nr <= receive_byte_nr + 1; + receive_command((receive_byte_nr*8)+7 downto (receive_byte_nr*8)) <= rx_byte; + if (receive_byte_nr = 3) then + receive_byte_nr <= 0; + receive_valid <= '1'; + end if; + end if; + + end if; + end process; + + + istringprocessor: entity tb.estringprocessor + generic map + ( + clk_speed => clk_speed + ) + port map + ( + ready => idle, + tx_command => proc_command, + tx_bytes => proc_bytes, + tx_enable => proc_enable, + rx_command => receive_command, + rx_valid => receive_valid + ); + + + itbrs232_transmitter : entity rs232.tbrs232_transmitter + port map + ( + clk => clk, + busy => tx_busy, + sendbyte => sendbyte, + enable => tx_enable, + tx => command_in + ); + + itbrs232_receiver : entity rs232.tbrs232_receiver + port map + ( + clk => clk, + rx_byte => rx_byte, + valid => rx_valid, + rx => command_out + ); + + + + +end architecture; \ No newline at end of file diff --git a/sim/tests/gb_bootrom.lua b/sim/tests/gb_bootrom.lua new file mode 100644 index 0000000..7a41d6f --- /dev/null +++ b/sim/tests/gb_bootrom.lua @@ -0,0 +1,15 @@ +require("vsim_comm") +require("luareg") + +wait_ns(10000) + +reg_set(0, gameboy.Reg_GB_on) + +reg_set_file("tests\\tetris.gb", 0, 0, 0) + +wait_ns(10000) +reg_set(1, gameboy.Reg_GB_on) + +print("GB ON") + +brk() \ No newline at end of file diff --git a/sim/tests/luareg.lua b/sim/tests/luareg.lua new file mode 100644 index 0000000..6491c6e --- /dev/null +++ b/sim/tests/luareg.lua @@ -0,0 +1,44 @@ +--space.name = {address, upper, lower, size, default} +gameboy = {} +gameboy.Reg_GB_on = {1056768,0,0,1,0,"gameboy.Reg_GB_on"} -- on = 1 +gameboy.Reg_GB_lockspeed = {1056769,0,0,1,0,"gameboy.Reg_GB_lockspeed"} -- 1 = 100% speed +gameboy.Reg_GB_flash_1m = {1056770,0,0,1,0,"gameboy.Reg_GB_flash_1m"} +gameboy.Reg_GB_CyclePrecalc = {1056771,15,0,1,100,"gameboy.Reg_GB_CyclePrecalc"} +gameboy.Reg_GB_CyclesMissing = {1056772,31,0,1,0,"gameboy.Reg_GB_CyclesMissing"} +gameboy.Reg_GB_BusAddr = {1056773,27,0,1,0,"gameboy.Reg_GB_BusAddr"} +gameboy.Reg_GB_BusRnW = {1056773,28,28,1,0,"gameboy.Reg_GB_BusRnW"} +gameboy.Reg_GB_BusACC = {1056773,30,29,1,0,"gameboy.Reg_GB_BusACC"} +gameboy.Reg_GB_BusWriteData = {1056774,31,0,1,0,"gameboy.Reg_GB_BusWriteData"} +gameboy.Reg_GB_BusReadData = {1056775,31,0,1,0,"gameboy.Reg_GB_BusReadData"} +gameboy.Reg_GB_MaxPakAddr = {1056776,24,0,1,0,"gameboy.Reg_GB_MaxPakAddr"} +gameboy.Reg_GB_VsyncSpeed = {1056777,31,0,1,0,"gameboy.Reg_GB_VsyncSpeed"} +gameboy.Reg_GB_KeyUp = {1056778,0,0,1,0,"gameboy.Reg_GB_KeyUp"} +gameboy.Reg_GB_KeyDown = {1056778,1,1,1,0,"gameboy.Reg_GB_KeyDown"} +gameboy.Reg_GB_KeyLeft = {1056778,2,2,1,0,"gameboy.Reg_GB_KeyLeft"} +gameboy.Reg_GB_KeyRight = {1056778,3,3,1,0,"gameboy.Reg_GB_KeyRight"} +gameboy.Reg_GB_KeyA = {1056778,4,4,1,0,"gameboy.Reg_GB_KeyA"} +gameboy.Reg_GB_KeyB = {1056778,5,5,1,0,"gameboy.Reg_GB_KeyB"} +gameboy.Reg_GB_KeyL = {1056778,6,6,1,0,"gameboy.Reg_GB_KeyL"} +gameboy.Reg_GB_KeyR = {1056778,7,7,1,0,"gameboy.Reg_GB_KeyR"} +gameboy.Reg_GB_KeyStart = {1056778,8,8,1,0,"gameboy.Reg_GB_KeyStart"} +gameboy.Reg_GB_KeySelect = {1056778,9,9,1,0,"gameboy.Reg_GB_KeySelect"} +gameboy.Reg_GB_cputurbo = {1056780,0,0,1,0,"gameboy.Reg_GB_cputurbo"} -- 1 = cpu free running, all other 16 mhz +gameboy.Reg_GB_SramFlashEna = {1056781,0,0,1,0,"gameboy.Reg_GB_SramFlashEna"} -- 1 = enabled, 0 = disable (disable for copy protection in some games) +gameboy.Reg_GB_MemoryRemap = {1056782,0,0,1,0,"gameboy.Reg_GB_MemoryRemap"} -- 1 = enabled, 0 = disable (enable for copy protection in some games) +gameboy.Reg_GB_SaveState = {1056783,0,0,1,0,"gameboy.Reg_GB_SaveState"} +gameboy.Reg_GB_LoadState = {1056784,0,0,1,0,"gameboy.Reg_GB_LoadState"} +gameboy.Reg_GB_FrameBlend = {1056785,0,0,1,0,"gameboy.Reg_GB_FrameBlend"} -- mix last and current frame +gameboy.Reg_GB_Pixelshade = {1056786,2,0,1,0,"gameboy.Reg_GB_Pixelshade"} -- pixel shade 1..4, 0 = off +gameboy.Reg_GB_SaveStateAddr = {1056787,25,0,1,0,"gameboy.Reg_GB_SaveStateAddr"} -- address to save/load savestate +gameboy.Reg_GB_Rewind_on = {1056788,0,0,1,0,"gameboy.Reg_GB_Rewind_on"} +gameboy.Reg_GB_Rewind_active = {1056789,0,0,1,0,"gameboy.Reg_GB_Rewind_active"} +gameboy.Reg_GB_DEBUG_CPU_PC = {1056800,31,0,1,0,"gameboy.Reg_GB_DEBUG_CPU_PC"} +gameboy.Reg_GB_DEBUG_CPU_MIX = {1056801,31,0,1,0,"gameboy.Reg_GB_DEBUG_CPU_MIX"} +gameboy.Reg_GB_DEBUG_IRQ = {1056802,31,0,1,0,"gameboy.Reg_GB_DEBUG_IRQ"} +gameboy.Reg_GB_DEBUG_DMA = {1056803,31,0,1,0,"gameboy.Reg_GB_DEBUG_DMA"} +gameboy.Reg_GB_DEBUG_MEM = {1056804,31,0,1,0,"gameboy.Reg_GB_DEBUG_MEM"} +gameboy.Reg_GB_CHEAT_FLAGS = {1056810,31,0,1,0,"gameboy.Reg_GB_CHEAT_FLAGS"} +gameboy.Reg_GB_CHEAT_ADDRESS = {1056811,31,0,1,0,"gameboy.Reg_GB_CHEAT_ADDRESS"} +gameboy.Reg_GB_CHEAT_COMPARE = {1056812,31,0,1,0,"gameboy.Reg_GB_CHEAT_COMPARE"} +gameboy.Reg_GB_CHEAT_REPLACE = {1056813,31,0,1,0,"gameboy.Reg_GB_CHEAT_REPLACE"} +gameboy.Reg_GB_CHEAT_RESET = {1056814,0,0,1,0,"gameboy.Reg_GB_CHEAT_RESET"} \ No newline at end of file diff --git a/sim/tests/vsim_comm.lua b/sim/tests/vsim_comm.lua new file mode 100644 index 0000000..2ae83c4 --- /dev/null +++ b/sim/tests/vsim_comm.lua @@ -0,0 +1,417 @@ +function string:split(sep) + local sep, fields = sep or ":", {} + local pattern = string.format("([^%s]+)", sep) + self:gsub(pattern, function(c) fields[#fields+1] = c end) + return fields +end + +function conv_neg(x) + if (x < 0) then + return (2147483647 + (2147483649 - ((x * (-1)) ))) + else + return x + end +end + +function conv_to_neg(x) + if (x > 2147483647) then + return x - (2* 2147483648) + else + return x + end +end + +function binary_and(a,b) + local r = 0 + local neg = 1 + if (a < 0) then + a = a * (-1) + neg = neg * (-1) + end + if (b < 0) then + b = b * (-1) + neg = neg * (-1) + end + for i = 31, 0, -1 do + local x = 2^i + if (a >= x and b>=x) then + r = r + 2^i + end + if (a >= x) then + a = a - x + end + if (b >= x) then + b = b - x + end + end + return r * neg +end + +function binary_or(a,b) + local r = 0 +local neg = 1 + if (a < 0) then + a = a * (-1) + neg = neg * (-1) + end + if (b < 0) then + b = b * (-1) + neg = neg * (-1) + end + for i = 31, 0, -1 do + local x = 2^i + if (a >= x or b>=x) then + r = r + 2^i + end + if (a >= x) then + a = a - x + end + if (b >= x) then + b = b - x + end + end + return r * neg +end +-------------- +-- file access +-------------- + +inputfile = "../input.txt" +outputfile = "../output.txt" + +blockwritesize = 128 +wait_on_writeblock = true + +function write_one(command) + + local file = nil + while (file == nil) do + file=io.open(inputfile,"a+") + end + + io.output(file) + + io.write(command) + io.write("\n") + + io.close(file) + +end + +function read_one(command) + + local read_line = "" + + command = string.sub(command, 1, #command - 1) + + --print("#command: "..#command) + + if (endpointer > 1000) then + endpointer = endpointer - 1000 + else + endpointer = 0 + end + + while read_line ~= command do + + local file = nil + while (file == nil) do + file=io.open(outputfile,"r") + end + + file:seek("set", endpointer) + + local line = file:read() + + while (line ~= nil) do + + local buf_line = line + + local ix = #command + 1 + while ix < #buf_line do + if (string.sub(buf_line,ix,ix) == "#") then + break + end + ix = ix + 1 + end + + result = string.sub(buf_line,ix + 2,#buf_line - 1) + buf_line = string.sub(buf_line,0,ix - 2) + + --print("########") + --print(line) + --print(command) + --print(buf_line) + --print("Result:"..result) + + if (buf_line == command) then + read_line = buf_line + break; + end + + line = file:read() + + end + + new_endpointer = file:seek("end") + io.close(file) + + end + + endpointer = new_endpointer + + return result +end + +------------------- +-- connect commands +------------------- + +function reg_get_block_connection(reg, index, size) + command_nr = command_nr + 1 + if (index == null) then index = 0 end + + block = {} + local readindex = 0 + local dataleft = size + while (dataleft > 0) do + + local blocksize = math.min(dataleft, 128) + + command = "get # "..command_nr.." # "..process_id.." # ".."1".." # "..blocksize.." # "..(reg[1] + index).."&" + write_one(command) + values = read_one(command) + + blockraw = values:split("#") + for i = 1, blocksize do + block[readindex] = tonumber(blockraw[i]) + readindex = readindex + 1 + end + + dataleft = dataleft - blocksize + index = index + blocksize + + end + + return block +end + +function reg_get_connection(reg, index) + block = reg_get_block_connection(reg, index, 1) + return block[0] +end + +function reg_set_block_connection(values, reg, index) + if (index == null) then index = 0 end + command_nr = command_nr + 1 + local length = (#values + 1) + command = "set # "..command_nr.." # "..process_id.." # ".."1".." # "..math.min(blockwritesize, length).." # "..(reg[1] + index) + local writecount = 0 + for i = 0, #values do + value = conv_to_neg(values[i]) + command = command.." # "..value + writecount = writecount + 1 + if (writecount == blockwritesize) then + command = command.."&" + write_one(command) + if (wait_on_writeblock) then + read_one(command) + end + writecount = 0 + index = index + blockwritesize + command_nr = command_nr + 1 + length = length - blockwritesize + command = "set # "..command_nr.." # "..process_id.." # ".."1".." # "..math.min(blockwritesize, length).." # "..(reg[1] + index) + end + end + if (length > 0) then + command = command.."&" + write_one(command) + if (wait_on_writeblock) then + read_one(command) + end + end +end + +function reg_set_connection(value, reg, index) + values = {} + values[0] = value + reg_set_block_connection(values, reg, index) +end + +----------- +-- commands +----------- + +function reg_filter(reg, value) + + filter = 0 + for i = reg[3], reg[2] do + filter = filter + 2^i; + end + + value = conv_neg(value) + value = binary_and(value,filter); + value = value / (2 ^ reg[3]); + + return value + +end + +function reg_get(reg, index) + value = reg_get_connection(reg, index) + + value = reg_filter(reg, value) + + return value +end + +function reg_get_block(reg, index, size) + + block = reg_get_block_connection(reg, index, size) + for i = 0, size - 1 do + value = block[i] + + filter = 0 + for i = reg[3], reg[2] do + filter = filter + 2^i; + end + + value = conv_neg(value) + value = binary_and(value,filter); + value = value / (2 ^ reg[3]); + + block[i] = value + end + + return block +end + +function reg_set_block(block, reg, index) + reg_set_block_connection(block, reg, index) +end + + +function reg_set(value, reg, index) + if (index == null) then index = 0 end + + if (value < 100) then + --print ("Address "..(reg[1] + index).."["..reg[2]..".."..reg[3].."] => "..value) + else + --print ("Address "..(reg[1] + index).."["..reg[2]..".."..reg[3].."] => 0x"..string.format("%X", value)) + end + + -- find out if parameter is the only one in this register + local singlereg = true + local fullname = reg[6] + local sectionname = fullname:split(".")[1] + local secttable = _G[sectionname] + for name, register in pairs(secttable) do + if (register[1] == reg[1]) then + if (register[6] ~= reg[6]) then + singlereg = false + break + end + end + end + + oldval = 0 + + if (singlereg == false) then + + oldval = reg_get_connection(reg, index) + + --print ("oldval= "..oldval.." at "..(reg[1]+index)) + + filter = 0 + for i = 0, reg[3] - 1 do + filter = filter + 2^i; + end + for i = reg[2] + 1, 31 do + filter = filter + 2^i; + end + oldval = binary_and(oldval,filter); + + --print ("oldval filtered= "..oldval.." at "..(reg[1]+index).." with filter "..filter) + + end + + value = value * (2 ^ reg[3]) + value = binary_or(value, oldval); + + reg_set_connection(value, reg, index) +end + + +function compare_reg(target, reg, index) + if (index == null) then index = 0 end + value = reg_get(reg, index) + if (value == target) then + if (value < 100 and target < 100) then + print (reg[6].." -> Address "..(reg[1] + index).."["..reg[2]..".."..reg[3].."] = "..value.." - OK") + else + print (reg[6].." -> Address "..(reg[1] + index).."["..reg[2]..".."..reg[3].."] = 0x"..string.format("%X", value).." - OK") + end + return true + else + testerrorcount = testerrorcount + 1 + if (value < 100 and target < 100) then + print (reg[6].." -> Address "..(reg[1] + index).."["..reg[2]..".."..reg[3].."] = "..value.." | should be = "..target.." - ERROR") + else + print (reg[6].." -> Address "..(reg[1] + index).."["..reg[2]..".."..reg[3].."] = 0x"..string.format("%X", tonumber(value)).." | should be = 0x"..string.format("%X", tonumber(target)).." - ERROR") + end + return false; + end +end + +function reg_set_file(filename, baseaddress, index, endian) + if (index == null) then index = 0 end + if (endian == null) then endian = 0 end + command_nr = command_nr + 1 + command = "fil # "..command_nr.." # "..process_id.." # "..endian.." # "..(baseaddress + index).." # "..filename + command = command.."&" + write_one(command) + if (wait_on_writeblock) then + read_one(command) + end +end + +function wait_ns(waittime) + command_nr = command_nr + 1 + command = "wtn # "..command_nr.." # "..process_id.." # "..waittime.."&" + write_one(command) + read_one(command) +end + +function brk() + command_nr = command_nr + 1 + command = "brk&" + write_one(command) +end + +function sleep(n) -- seconds + local t0 = os.clock() + while os.clock() - t0 <= n do end +end + +---------- +-- init +---------- + + +seed = os.time() + os.clock() * 1000 +math.randomseed(seed) +process_id = math.random(2147483647) +process_id = math.random(2147483647) +process_id = math.random(2147483647) +process_id = math.random(2147483647) +process_id = math.random(2147483647) + +endpointer = 0 + +command_nr = 0 + + + + + diff --git a/sim/vcom_all.bat b/sim/vcom_all.bat new file mode 100644 index 0000000..6b52b8e --- /dev/null +++ b/sim/vcom_all.bat @@ -0,0 +1,55 @@ + +vcom -93 -quiet -work sim/tb ^ +src/tb/globals.vhd + +vcom -93 -quiet -work sim/mem ^ +src/mem/SyncFifo.vhd + +vcom -quiet -work sim/rs232 ^ +src/rs232/rs232_receiver.vhd ^ +src/rs232/rs232_transmitter.vhd ^ +src/rs232/tbrs232_receiver.vhd ^ +src/rs232/tbrs232_transmitter.vhd + +vcom -quiet -work sim/procbus ^ +src/procbus/proc_bus.vhd ^ +src/procbus/testprocessor.vhd + +vcom -quiet -work sim/reg_map ^ +src/reg_map/reg_gameboy.vhd + +vcom -quiet -work sim/gameboy ^ +../rtl/T80/T80_Pack.vhd ^ +../rtl/T80/T80_Reg.vhd ^ +../rtl/T80/T80_MCode.vhd ^ +../rtl/T80/T80_ALU.vhd ^ +../rtl/T80/T80.vhd ^ +../rtl/T80/GBse.vhd + +vcom -quiet -work sim/gameboy ^ +../rtl/gbc_snd.vhd ^ +../rtl/boot_rom.vhd ^ +../rtl/speedcontrol.vhd ^ +../rtl/spram.vhd + +vlog -sv -quiet -work sim/gameboy ^ +../rtl/sprites.v ^ +../rtl/timer.v ^ +../rtl/video.v ^ +../rtl/link.v ^ +../rtl/hdma.v + +vlog -sv -O0 -quiet -work sim/gameboy ^ +../rtl/gb.v + +vlog -sv -quiet -work sim/gameboy ^ +src/gameboy/cheatcodes.sv + +vcom -quiet -work sim/tb ^ +src/tb/stringprocessor.vhd ^ +src/tb/tb_interpreter.vhd ^ +src/tb/framebuffer.vhd ^ +src/tb/gb_bios.vhd ^ +src/tb/sdram_model.vhd ^ +src/tb/tb.vhd + diff --git a/sim/vmap_all.bat b/sim/vmap_all.bat new file mode 100644 index 0000000..ef3cbf6 --- /dev/null +++ b/sim/vmap_all.bat @@ -0,0 +1,23 @@ +RMDIR /s /q sim +MKDIR sim + +vmap altera_mf altera_mf + +vlib sim/mem +vmap mem sim/mem + +vlib sim/rs232 +vmap rs232 sim/rs232 + +vlib sim/procbus +vmap procbus sim/procbus + +vlib sim/reg_map +vmap reg_map sim/reg_map + +vlib sim/gameboy +vmap gameboy sim/gameboy + +vlib sim/tb +vmap tb sim/tb + diff --git a/sim/vsim_start.bat b/sim/vsim_start.bat new file mode 100644 index 0000000..e09ee53 --- /dev/null +++ b/sim/vsim_start.bat @@ -0,0 +1 @@ +vsim -novopt tb.etb -t 1ps -suppress 8684 \ No newline at end of file From 2d965da32376f5c5bf7378ae5e6e7707736fbf09 Mon Sep 17 00:00:00 2001 From: Robert Peip Date: Tue, 15 Dec 2020 15:24:33 +0100 Subject: [PATCH 2/2] add fastforward and OSD-Pause feature --- Gameboy.sv | 40 +++++++++++++++++------- files.qip | 1 + rtl/gb.v | 3 +- rtl/speedcontrol.vhd | 73 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 12 deletions(-) create mode 100644 rtl/speedcontrol.vhd diff --git a/Gameboy.sv b/Gameboy.sv index fd8cb48..cf040fe 100644 --- a/Gameboy.sv +++ b/Gameboy.sv @@ -151,7 +151,7 @@ assign AUDIO_MIX = status[8:7]; // 0 1 2 3 // 01234567890123456789012345678901 // 0123456789ABCDEFGHIJKLMNOPQRSTUV -// XXXXXXXXXXXXXXXXXXXXX XX +// XXXXXXXXXXXXXXXXXXXXX XXXXX `include "build_id.v" localparam CONF_STR = { @@ -179,9 +179,12 @@ localparam CONF_STR = { "-;", "OB,Boot,Normal,Fast;", "O6,Link Port,Disabled,Enabled;", + "-;", + "OPQ,FastForward Sound,Normal,Hack,Off;", + "OR,Pause when OSD is open,Off,On;", "-;", "R0,Reset;", - "J1,A,B,Select,Start;", + "J1,A,B,Select,Start,FastForward;", "V,v",`BUILD_DATE }; @@ -555,6 +558,9 @@ always @(posedge clk_sys) if(reset) begin else if(cart_download) isGBC <= !filetype[7:4]; end +wire [15:0] GB_AUDIO_L; +wire [15:0] GB_AUDIO_R; + // the gameboy itself gb gb ( .reset ( reset ), @@ -562,6 +568,7 @@ gb gb ( .clk_sys ( clk_sys ), .ce ( ce_cpu ), // the whole gameboy runs on 4mhnz .ce_2x ( ce_cpu2x ), // ~8MHz in dualspeed mode (GBC) + .ce_sound ( ce_sound ), // ~8Mhz if not fastforward .fast_boot ( status[11] ), @@ -583,8 +590,8 @@ gb gb ( .gbc_bios_do ( bios_do ), // audio - .audio_l ( AUDIO_L ), - .audio_r ( AUDIO_R ), + .audio_l ( GB_AUDIO_L ), + .audio_r ( GB_AUDIO_R ), // interface to the lcd .lcd_clkena ( lcd_clkena ), @@ -609,6 +616,9 @@ gb gb ( .gg_available(gg_available) ); +assign AUDIO_L = (joystick_0[8] && status[26]) ? 16'd0 : GB_AUDIO_L; +assign AUDIO_R = (joystick_0[8] && status[26]) ? 16'd0 : GB_AUDIO_R; + // the lcd to vga converter wire [7:0] video_r, video_g, video_b; wire video_hs, video_vs; @@ -739,14 +749,22 @@ video_mixer #(.LINE_LENGTH(200), .GAMMA(1)) video_mixer //////////////////////////////// CE //////////////////////////////////// -reg ce_cpu, ce_cpu2x; -always @(negedge clk_sys) begin - reg [2:0] div = 0; - div <= div + 1'd1; - ce_cpu2x <= !div[1:0]; - ce_cpu <= !div[2:0]; -end +wire ce_cpu, ce_cpu2x, ce_sound, ce_soundhack; +wire cart_act = cart_wr | cart_rd; + +speedcontrol speedcontrol +( + .clk_sys (clk_sys), + .pause (status[27] && OSD_STATUS && !ioctl_download), + .speedup (joystick_0[8] && !ioctl_download && !OSD_STATUS), + .cart_act (cart_act), + .ce (ce_cpu), + .ce_2x (ce_cpu2x), + .ce_2xNormal (ce_soundhack) +); + +assign ce_sound = status[25] ? ce_soundhack : ce_cpu2x; ///////////////////////////// GBC BIOS ///////////////////////////////// diff --git a/files.qip b/files.qip index f215bd4..80a7915 100644 --- a/files.qip +++ b/files.qip @@ -9,6 +9,7 @@ set_global_assignment -name VERILOG_FILE rtl/timer.v set_global_assignment -name VERILOG_FILE rtl/sprites.v set_global_assignment -name VERILOG_FILE rtl/lcd.v set_global_assignment -name VHDL_FILE rtl/gbc_snd.vhd +set_global_assignment -name VHDL_FILE rtl/speedcontrol.vhd set_global_assignment -name VERILOG_FILE rtl/gb.v set_global_assignment -name VERILOG_FILE rtl/hdma.v set_global_assignment -name VERILOG_FILE rtl/link.v diff --git a/rtl/gb.v b/rtl/gb.v index 89fbdc3..688cbd0 100644 --- a/rtl/gb.v +++ b/rtl/gb.v @@ -25,6 +25,7 @@ module gb ( input clk_sys, input ce, input ce_2x, + input ce_sound, input fast_boot, input [7:0] joystick, @@ -267,7 +268,7 @@ wire audio_wr = !cpu_wr_n && sel_audio; gbc_snd audio ( .clk ( clk_sys ), - .ce ( ce_2x ), + .ce ( ce_sound ), .reset ( reset_r ), .is_gbc ( isGBC ), diff --git a/rtl/speedcontrol.vhd b/rtl/speedcontrol.vhd new file mode 100644 index 0000000..b51cd6a --- /dev/null +++ b/rtl/speedcontrol.vhd @@ -0,0 +1,73 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +entity speedcontrol is + port + ( + clk_sys : in std_logic; + pause : in std_logic; + speedup : in std_logic; + cart_act : in std_logic; + ce : out std_logic := '0'; + ce_2x : out std_logic := '0'; + ceNormal : out std_logic := '0'; + ce_2xNormal : out std_logic := '0' + ); +end entity; + +architecture arch of speedcontrol is + + signal clkdiv : unsigned(2 downto 0) := (others => '0'); + signal nextdiv : unsigned(2 downto 0) := (others => '0'); + + signal clkdivNormal : unsigned(2 downto 0) := (others => '0'); + + signal cart_act_1 : std_logic := '0'; + +begin + + process(clk_sys) + begin + if falling_edge(clk_sys) then + if (pause = '1') then + + ce <= '0'; + ce_2x <= '0'; + ceNormal <= '0'; + ce_2xNormal <= '0'; + + else + + clkdiv <= clkdiv + 1; + clkdivNormal <= clkdivNormal + 1; + + -- generation for speed depending on speedup + cart_act_1 <= cart_act; + + if (clkdiv = "000") then ce <= '1'; else ce <= '0'; end if; + if ((nextdiv = "111" and clkdiv(1 downto 0) = "00") or nextdiv = "001") then ce_2x <= '1'; else ce_2x <= '0'; end if; + + if (clkdiv = nextdiv and (clkdiv = "111" or cart_act = '0')) then + clkdiv <= "000"; + if (speedup = '1') then + nextdiv <= "001"; + else + nextdiv <= "111"; + end if; + end if; + + if (cart_act = '1' and cart_act_1 = '0') then + nextdiv <= "111"; + end if; + + -- generation for non speed up base, used e.g. for sound hack + if (clkdivNormal = "000") then ceNormal <= '1'; else ceNormal <= '0'; end if; + if (clkdivNormal(1 downto 0) = "00") then ce_2xNormal <= '1'; else ce_2xNormal <= '0'; end if; + + end if; + + end if; + end process; + +end architecture;