`include "tune.v" // PentEvo project (c) NedoPC 2008-2009 // // Z80 memory manager: routes ROM/RAM accesses, makes wait-states for 14MHz or stall condition, etc. // // // clk _/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\ // | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | // zclk /```\___/```\___/```\___/```````\_______/```````\_______/```````````````\_______________/```````````````\_______________/` // | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | // zpos `\___/```\___/```\___/```\___________/```\___________/```\___________________________/```\___________________________/```\ // | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | // zneg _/```\___/```\___/```\_______/```\___________/```\___________________/```\___________________________/```\________________ module zmem ( input wire clk, input wire c1, c2, c3, input wire zneg, // strobes which show positive and negative edges of zclk // Z80 input wire rst, input wire [15:0] za, output wire [ 7:0] zd_out, // output to Z80 bus output wire zd_ena, // output to Z80 bus enable input wire opfetch, input wire opfetch_s, input wire memrd, input wire memwr, input wire memwr_s, input wire [ 1:0] turbo, // 2'b00 - 3.5, // 2'b01 - 7.0, // 2'b1x - 14.0 input wire [3:0] cache_en, input wire [3:0] memconf, input wire [31:0] xt_page, output wire [4:0] rompg, output wire csrom, output wire romoe_n, output wire romwe_n, output wire dos, output wire dos_on, output wire dos_off, output wire vdos, output reg pre_vdos, input wire vdos_on, input wire vdos_off, // DRAM output wire cpu_req, output wire [20:0] cpu_addr, output wire cpu_wrbsel, input wire [15:0] cpu_rddata, input wire cpu_next, input wire cpu_strobe, input wire cpu_latch, output wire cpu_stall // for zclock ); // controls wire rom128 = memconf[0]; wire w0_we = memconf[1]; wire w0_map_n = memconf[2]; wire w0_ram = memconf[3]; // pager wire [7:0] xtpage[0:3]; assign xtpage[0] = vdos ? 8'hFF : {xt_page[7:2], w0_map_n ? xt_page[1:0] : {~dos, rom128}}; assign xtpage[1] = xt_page[15:8]; assign xtpage[2] = xt_page[23:16]; assign xtpage[3] = xt_page[31:24]; wire [1:0] win = za[15:14]; wire win0 = ~|win; wire ramwr_en = !win0 || w0_we || vdos; wire rom_n_ram = win0 && !w0_ram && !vdos; wire [7:0] page = xtpage[win]; // DOS signal control reg dos_r; assign dos_on = win0 && opfetch_s && (za[13:8]==6'h3D) && rom128 && !w0_map_n; assign dos_off = !win0 && opfetch_s && !vdos; assign dos = dos_on ? 1'b1 : (dos_off ? 1'b0 : dos_r); // to make dos appear 1 clock earlier than dos_r always @(posedge clk) if (rst) dos_r <= 1'b0; else if (dos_off) dos_r <= 1'b0; else if (dos_on) dos_r <= 1'b1; // VDOS signal control // vdos switching is delayed till next opfetch due to INIR that writes right after iord cycle reg vdos_r; assign vdos = opfetch ? pre_vdos : vdos_r; // vdos appears as soon as first opfetch always @(posedge clk) if (rst || vdos_off) begin pre_vdos <= 1'b0; vdos_r <= 1'b0; end else if (vdos_on) pre_vdos <= 1'b1; else if (opfetch_s) vdos_r <= pre_vdos; // RAM wire cache_hit_en; assign zd_ena = !rom_n_ram && memrd; wire ramreq = ((memrd && !cache_hit_en) || (memwr && ramwr_en)); // address, data in and data out wire [15:0] cache_d; wire cache_v; assign cpu_wrbsel = za[0]; assign cpu_addr[20:0] = {page, za[13:1]}; wire [15:0] mem_d = cpu_latch ? cpu_rddata : cache_d; assign zd_out = trdos_3dxx_hit ? trdos_3dxx_do : ~cpu_wrbsel ? mem_d[7:0] : mem_d[15:8]; // Z80 controls // 7/3.5MHz support reg ramreq_r; reg pre_ramreq_r; reg pending_cpu_req; reg stall14_cycrd; reg stall14_fin; wire dram_beg = ramreq && !pre_ramreq_r && zneg; wire cpureq_357 = ramreq && !ramreq_r; wire cpureq_14 = dram_beg || pending_cpu_req; wire stall357 = cpureq_357 && !cpu_next; wire stall14_ini = dram_beg && (!cpu_next || opfetch || memrd); // no wait at all in write cycles, if next dram cycle is available wire stall14_cyc = memrd ? stall14_cycrd : !cpu_next; wire stall14 = stall14_ini || stall14_cyc || stall14_fin; wire turbo14 = turbo[1]; assign cpu_req = turbo14 ? cpureq_14 : cpureq_357; assign cpu_stall = turbo14 ? stall14 : stall357; // 14MHz support // wait tables: // // M1 opcode fetch, dram_beg concurs with: // c3: +3 // c2: +4 // c1: +5 // c0: +6 // // memory read, dram_beg concurs with: // c3: +2 // c2: +3 // c1: +4 // c0: +5 // // memory write: no wait // // special case: if dram_beg pulses 1 when cpu_next is 0, // unconditional wait has to be performed until cpu_next is 1, and // then wait as if dram_beg would concur with c0 // memrd, opfetch - wait till c3 && cpu_next, // memwr - wait till cpu_next always @(posedge clk) if (c3 && !cpu_stall) ramreq_r <= ramreq; always @(posedge clk) if (zneg) pre_ramreq_r <= ramreq; always @(posedge clk) if (rst) pending_cpu_req <= 1'b0; else if (cpu_next && c3) pending_cpu_req <= 1'b0; else if (dram_beg) pending_cpu_req <= 1'b1; always @(posedge clk) if (rst) stall14_cycrd <= 1'b0; else if (cpu_next && c3) stall14_cycrd <= 1'b0; else if (dram_beg && (!c3 || !cpu_next) && (opfetch || memrd)) stall14_cycrd <= 1'b1; always @(posedge clk) if (rst) stall14_fin <= 1'b0; else if (stall14_fin && ((opfetch && c1) || (memrd && c2))) stall14_fin <= 1'b0; else if (cpu_next && c3 && cpu_req && (opfetch || memrd)) stall14_fin <= 1'b1; // cache wire [13:0] cache_a; wire [13:0] cpu_hi_addr = {rom_n_ram, page[7:0], za[13:9]}; // wire cache_hit = (ch_addr[7:2] != 6'b011100) && (cpu_hi_addr == cache_a) && cache_v; // debug for BM wire cache_hit = (cpu_hi_addr == cache_a) && cache_v; // asynchronous signal meaning that address requested by CPU is cached and valid assign cache_hit_en = cache_hit && (cache_en[win] || rom_n_ram); wire cache_inv = cache_hit && memwr_s && ramwr_en; // cache invalidation should be only performed if write happens to cached address wire cache_inv_rst = cache_inv || rst; // rst may happen at the same time with cpu_strobe, which leads incorrect data will be written to 0h address wire [7:0] ch_addr = cpu_addr[7:0]; // wire [14:0] cpu_hi_addr = {page[7:0], za[13:7]}; // wire [14:0] cache_a; // wire [7:0] ch_addr = {2'b0, cpu_addr[5:0]}; dpram #(.DATAWIDTH(16), .ADDRWIDTH(8)) cache_data ( .clock(clk), .address_a(ch_addr), .data_a(cpu_rddata), .wren_a(cpu_strobe), .address_b(ch_addr), .q_b(cache_d) ); dpram #(.DATAWIDTH(15), .ADDRWIDTH(8)) cache_addr ( .clock(clk), .address_a(ch_addr), .data_a({!cache_inv_rst, cpu_hi_addr}), .wren_a(cpu_strobe || cache_inv), .address_b(ch_addr), .q_b({cache_v, cache_a}) ); /* altdpram cache_data ( .inclock (clk), .outclock (clk), .data (cpu_rddata), .rdaddress (ch_addr), .wraddress (ch_addr), .wren (cpu_strobe), .q (cache_d), .aclr (1'b0), .byteena (1'b1), .inclocken (1'b1), .outclocken (1'b1), .rdaddressstall (1'b0), .rden (1'b1), .wraddressstall (1'b0) ); defparam cache_data.indata_aclr = "OFF", cache_data.indata_reg = "INCLOCK", cache_data.intended_device_family = "ACEX1K", cache_data.lpm_type = "altdpram", cache_data.outdata_aclr = "OFF", cache_data.outdata_reg = "OUTCLOCK", cache_data.rdaddress_aclr = "OFF", cache_data.rdaddress_reg = "UNREGISTERED", cache_data.rdcontrol_aclr = "OFF", cache_data.rdcontrol_reg = "UNREGISTERED", cache_data.width = 16, cache_data.widthad = 8, cache_data.wraddress_aclr = "OFF", cache_data.wraddress_reg = "INCLOCK", cache_data.wrcontrol_aclr = "OFF", cache_data.wrcontrol_reg = "INCLOCK"; altdpram cache_addr ( .inclock (clk), .outclock (clk), .data ({!cache_inv, cpu_hi_addr}), .rdaddress (ch_addr), .wraddress (ch_addr), .wren (cpu_strobe || cache_inv), .q ({cache_v, cache_a}), .aclr (1'b0), .byteena (1'b1), .inclocken (1'b1), .outclocken (1'b1), .rdaddressstall (1'b0), .rden (1'b1), .wraddressstall (1'b0) ); defparam cache_addr.indata_aclr = "OFF", cache_addr.indata_reg = "INCLOCK", cache_addr.intended_device_family = "ACEX1K", cache_addr.lpm_type = "altdpram", cache_addr.outdata_aclr = "OFF", cache_addr.outdata_reg = "OUTCLOCK", cache_addr.rdaddress_aclr = "OFF", cache_addr.rdaddress_reg = "UNREGISTERED", cache_addr.rdcontrol_aclr = "OFF", cache_addr.rdcontrol_reg = "UNREGISTERED", cache_addr.width = 14, cache_addr.widthad = 8, cache_addr.wraddress_aclr = "OFF", cache_addr.wraddress_reg = "INCLOCK", cache_addr.wrcontrol_aclr = "OFF", cache_addr.wrcontrol_reg = "INCLOCK"; */ // ROM chip assign csrom = rom_n_ram; assign romoe_n = !memrd; assign romwe_n = !(memwr && w0_we); assign rompg = xtpage[0][4:0]; // XXX ugly workarround // There are SDRAM latency-related issues with fetching opcode from correct page when entering DOS // With current HDL design ROM page number isn't valid at moment of cpu_req asserting // That is, there is a chance that SDRAM controller will start read cycle with an incorrect address wire [7:0] trdos_3dxx_do; wire trdos_3dxx_hit = dos && csrom && rompg==5'h1 && za[13:8]==6'h3D; dpram #(.DATAWIDTH(8), .ADDRWIDTH(8), .MEM_INIT_FILE("rtl/z80/trdos504T_3DXX.mif")) trdos_3dxx ( .clock (clk), .address_a (za[7:0]), .q_a (trdos_3dxx_do) ); endmodule