HDMA: moved hdma to module

This commit is contained in:
Bruno Duarte Gouveia
2018-12-17 02:55:26 +00:00
parent e5ad47870b
commit c8e788e59c
4 changed files with 167 additions and 92 deletions

View File

@@ -373,5 +373,6 @@ set_global_assignment -name VERILOG_FILE sprite.v
set_global_assignment -name VERILOG_FILE lcd.v
set_global_assignment -name VHDL_FILE gbc_snd.vhd
set_global_assignment -name VERILOG_FILE gb.v
set_global_assignment -name VERILOG_FILE hdma.v
set_global_assignment -name SYSTEMVERILOG_FILE Gameboy.sv
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top

View File

@@ -374,5 +374,6 @@ set_global_assignment -name VERILOG_FILE sprite.v
set_global_assignment -name VERILOG_FILE lcd.v
set_global_assignment -name VHDL_FILE gbc_snd.vhd
set_global_assignment -name VERILOG_FILE gb.v
set_global_assignment -name VERILOG_FILE hdma.v
set_global_assignment -name SYSTEMVERILOG_FILE Gameboy.sv
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top

120
gb.v
View File

@@ -78,9 +78,9 @@ wire dma_sel_vram = dma_addr[15:13] == 3'b100; // 8k video ram at $800
wire dma_sel_iram = (dma_addr[15:14] == 2'b11) && (dma_addr[15:8] != 8'hff); // 8k internal ram at $c000
//HDMA can select from $0000 to $7ff0 or A000-DFF0
wire hdma_sel_rom = !hdma_addr[15]; // lower 32k are rom
wire hdma_sel_cram = hdma_addr[15:13] == 3'b101; // 8k cart ram at $a000
wire hdma_sel_iram = hdma_addr[15:13] == 3'b110; // 8k internal ram at $c000-$dff0
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
@@ -338,10 +338,9 @@ wire video_irq;
wire [7:0] video_do;
wire [12:0] video_addr;
wire [15:0] dma_addr;
wire [15:0] hdma_addr;
wire video_rd, dma_rd, hdma_rd;
wire video_rd, dma_rd;
wire [7:0] dma_data = dma_sel_iram?iram_do:dma_sel_vram?vram_do:cart_do;
wire [7:0] hdma_data = hdma_sel_iram?iram_do:cart_do;
video video (
.reset ( reset ),
@@ -400,94 +399,31 @@ end
// -------------------------- HDMA engine(GBC) ------------------------
// --------------------------------------------------------------------
//ff51-ff55 HDMA1-5 (GBC)
reg [7:0] hdma_source_h; // ff51
reg [3:0] hdma_source_l; // ff52 only top 4 bits used
reg [4:0] hdma_target_h; // ff53 only lowest 5 bits used
reg [3:0] hdma_target_l; // ff54 only top 4 bits used
reg hdma_mode; // ff55 bit 7 - 1=General Purpose DMA 0=H-Blank DMA
reg hdma_enabled; // ff55 !bit 7 when read
reg [6:0] hdma_length; // ff55 bit 6:0 - dma transfer length (hdma_length+1)*16 bytes
wire [15:0] hdma_source_addr;
wire [15:0] hdma_target_addr;
wire [7:0] hdma_do;
wire hdma_rd;
wire [7:0] hdma_data = hdma_sel_iram?iram_do:cart_do;
assign hdma_rd = hdma_active;
assign hdma_addr = { hdma_source_h,hdma_source_l,4'd0} + hdma_cnt[12:1];
reg hdma_active;
// it takes about 8us to transfer a block of 16 bytes. -> 500ns per byte -> 2Mhz
// 32 cycles in Normal Speed Mode, and 64 'fast' cycles in Double Speed Mode
reg [12:0] hdma_cnt;
reg [4:0] hdma_16byte_cnt; //16bytes*2
always @(posedge clk) begin
if(reset)
hdma_active <= 1'b0;
else begin
// writing the hdma register engages the dma engine
if(isGBC && !cpu_wr_n && (cpu_addr == 16'hff55)) begin
hdma_enabled <= 1'b1;
hdma_mode <= cpu_do[7];
hdma_length <= cpu_do[6:0];
hdma_cnt <= 12'd0;
hdma_16byte_cnt <= 5'h1f;
//TODO: disable HDMA in mode 1
end else if(hdma_mode==0) begin //mode 0 GDMA do the transfer in one go
hdma hdma(
.reset ( reset ),
.clk ( clk ),
if(hdma_cnt != (((hdma_length+1)*16)-1)*2) begin
hdma_active <= 1'b1;
hdma_cnt <= hdma_cnt + 1'd1;
hdma_16byte_cnt <= hdma_16byte_cnt - 1'd1;
if (!hdma_16byte_cnt)
hdma_length <= hdma_length - 1'd1;
end else begin
hdma_active <= 1'b0;
hdma_enabled <= 1'b0;
end
end else begin //mode 1 HDMA transfer 1 block (16bytes) in each H-Blank only
if(hdma_cnt != (((hdma_length+1)*16)-1)*2 && (lcd_mode==0) && hdma_enabled) begin //also check if hdma is enabled and in h-blank
//TODO: have to rethink this, maybe state machine
hdma_active <= 1'b0;
hdma_enabled <= 1'b0;
end
end
end
end
always @(posedge clk) begin
if(reset) begin
hdma_source_h <= 8'hFF;
hdma_source_l <= 4'hF;
hdma_target_h <= 5'h1F;
hdma_target_l <= 4'hF;
end else if(sel_hdma && !cpu_wr_n && isGBC) begin
case (cpu_addr[3:0])
4'd1: hdma_source_h <= cpu_do;
4'd2: hdma_source_l <= cpu_do[7:4];
4'd3: hdma_target_h <= cpu_do[4:0];
4'd4: hdma_target_l <= cpu_do[7:4];
endcase
end
end
wire [7:0] hdma_do = isGBC&&sel_hdma?
(cpu_addr[3:0]==4'd1)?hdma_source_h:
(cpu_addr[3:0]==4'd2)?{hdma_source_l,4'd0}:
(cpu_addr[3:0]==4'd3)?{3'd0,hdma_target_h}:
(cpu_addr[3:0]==4'd4)?{hdma_target_l,,4'd0}:
(cpu_addr[3:0]==4'd5 && hdma_enabled)?{1'b0,hdma_length}:
8'hFF:
8'hFF;
// cpu register interface
.sel_reg ( sel_hdma ),
.addr ( cpu_addr[3:0] ),
.wr ( !cpu_wr_n ),
.dout ( hdma_do ),
.din ( cpu_do ),
.lcd_mode ( lcd_mode ),
// dma connection
.hdma_rd ( hdma_rd ),
.hdma_source_addr ( hdma_source_addr ),
.hdma_target_addr ( hdma_target_addr )
);
// --------------------------------------------------------------------
// -------------------------- zero page ram ---------------------------

137
hdma.v Normal file
View File

@@ -0,0 +1,137 @@
module hdma(
input reset,
input clk, // 4 Mhz cpu clock
// cpu register interface
input sel_reg,
input [3:0] addr,
input wr,
output [7:0] dout,
input [7:0] din,
input [1:0] lcd_mode,
// dma connection
output hdma_rd,
output [15:0] hdma_source_addr,
output [15:0] hdma_target_addr
);
//ff51-ff55 HDMA1-5 (GBC)
reg [7:0] hdma_source_h; // ff51
reg [3:0] hdma_source_l; // ff52 only top 4 bits used
reg [4:0] hdma_target_h; // ff53 only lowest 5 bits used
reg [3:0] hdma_target_l; // ff54 only top 4 bits used
reg hdma_mode; // ff55 bit 7 - 1=General Purpose DMA 0=H-Blank DMA
reg hdma_enabled; // ff55 !bit 7 when read
reg [6:0] hdma_length; // ff55 bit 6:0 - dma transfer length (hdma_length+1)*16 bytes
assign dout = hdma_do;
assign hdma_rd = hdma_active;
assign hdma_source_addr = { hdma_source_h,hdma_source_l,4'd0} + hdma_cnt[12:1];
assign hdma_target_addr = { 3'd0,hdma_target_h,hdma_target_l,4'd0} + hdma_cnt[12:1];
reg hdma_active;
// it takes about 8us to transfer a block of 16 bytes. -> 500ns per byte -> 2Mhz
// 32 cycles in Normal Speed Mode, and 64 'fast' cycles in Double Speed Mode
reg [12:0] hdma_cnt;
reg [4:0] hdma_16byte_cnt; //16bytes*2
reg hdma_state;
parameter active=1'b0,wait_h=1'b1;
always @(posedge clk) begin
if(reset) begin
hdma_active <= 1'b0;
hdma_state <= wait_h;
hdma_enabled <= 1'b0;
end else begin
// writing the hdma register engages the dma engine
if(wr && (addr == 4'h5)) begin
if (hdma_mode == 1 && hdma_enabled && !din[7]) begin //terminate an active H-Blank transfer by writing zero to Bit 7 of FF55
hdma_state <= wait_h;
hdma_active <= 1'b0;
hdma_enabled <= 1'b0;
end else begin //normal trigger
hdma_enabled <= 1'b1;
hdma_mode <= din[7];
hdma_length <= din[6:0];
hdma_cnt <= 12'd0;
hdma_16byte_cnt <= 5'h1f;
if (din[7] == 1) hdma_state <= wait_h;
end
end
if (hdma_enabled) begin
if(hdma_mode==0) begin //mode 0 GDMA do the transfer in one go
if(hdma_cnt != (((hdma_length+1)*16)-1)*2) begin
hdma_active <= 1'b1;
hdma_cnt <= hdma_cnt + 1'd1;
hdma_16byte_cnt <= hdma_16byte_cnt - 1'd1;
if (!hdma_16byte_cnt)
hdma_length <= hdma_length - 1'd1;
end else begin
hdma_active <= 1'b0;
hdma_enabled <= 1'b0;
end
end else begin //mode 1 HDMA transfer 1 block (16bytes) in each H-Blank only
case (hdma_state)
wait_h:begin
if (lcd_mode == 2'b00 ) // Mode 00: h-blank
hdma_state <= active;
hdma_16byte_cnt <= 5'h1f;
end
active:begin
if(hdma_cnt != (((hdma_length+1)*16)-1)*2) begin
hdma_active <= 1'b1;
hdma_cnt <= hdma_cnt + 1'd1;
hdma_16byte_cnt <= hdma_16byte_cnt - 1'd1;
if (!hdma_16byte_cnt) begin
hdma_length <= hdma_length - 1'd1;
hdma_state <= wait_h;
end
end else begin
hdma_active <= 1'b0;
hdma_enabled <= 1'b0;
end
end
endcase
end
end
end
end
always @(posedge clk) begin
if(reset) begin
hdma_source_h <= 8'hFF;
hdma_source_l <= 4'hF;
hdma_target_h <= 5'h1F;
hdma_target_l <= 4'hF;
end else if(sel_reg && wr) begin
case (addr)
4'd1: hdma_source_h <= din;
4'd2: hdma_source_l <= din[7:4];
4'd3: hdma_target_h <= din[4:0];
4'd4: hdma_target_l <= din[7:4];
endcase
end
end
wire [7:0] hdma_do = sel_reg?
(addr==4'd1)?hdma_source_h:
(addr==4'd2)?{hdma_source_l,4'd0}:
(addr==4'd3)?{3'd0,hdma_target_h}:
(addr==4'd4)?{hdma_target_l,4'd0}:
(addr==4'd5 && hdma_enabled)?{1'b0,hdma_length}:
8'hFF:
8'hFF;
endmodule