URL
https://opencores.org/ocsvn/mcs-4/mcs-4/trunk
Subversion Repositories mcs-4
[/] [mcs-4/] [trunk/] [rtl/] [verilog/] [i4001/] [i4001.v] - Rev 6
Compare with Previous | Blame | View Log
`timescale 1ns / 1ps `default_nettype none //////////////////////////////////////////////////////////////////////// // // MCS-4 i4001 ROM and I/O Port module // // This module emulates the Intel 4001 ROM and I/O chip. To make the // most efficient use of FPGA block ram resources, the ROM storage // is implemented using an "i4001_rom" module, which can be connected // to multiple "i4001" modules via a rom_addr / rom_data bus. // // This file is part of the MCS-4 project hosted at OpenCores: // http://www.opencores.org/cores/mcs-4/ // // Copyright © 2021 by Reece Pollack <rrpollack@opencores.org> // // These materials are provided under the Creative Commons // "Attribution-NonCommercial-ShareAlike" (CC BY-NC-SA) Public License. // They are NOT "public domain", and are protected by copyright. // // This work based on materials provided by Intel Corporation and // others under the same license. See the file doc/License for // details of this license. // //////////////////////////////////////////////////////////////////////// module i4001 #( parameter [3:0] ROM_NUMBER = 4'd0, parameter [3:0] IO_OUTPUT = 4'b0000, parameter [3:0] IO_INVERT = 4'b0000, parameter [3:0] IO_PULLUP = 4'b0000, parameter [3:0] IO_PULLDOWN = 4'b0000 ) ( input wire sysclk, input wire clk1_pad, input wire clk2_pad, input wire sync_pad, input wire poc_pad, input wire cmrom_pad, inout tri [3:0] data_pad, inout wire [3:0] io_pad, input wire clear_pad, output wor [11:0] rom_addr, input wire [ 7:0] rom_data ); // FUTURE: Sync these to the sysclk domain wire clk1 = clk1_pad; wire clk2 = clk2_pad; wire sync = sync_pad; wire poc = poc_pad; wire cmrom = cmrom_pad; wire clear = clear_pad; // Identify the execution phases wire a12, a22, a32; wire m11, m12, m21, m22; wire x21, x22; timing_recovery timing_recovery ( .sysclk (sysclk), .clk1 (clk1), .clk2 (clk2), .sync (sync), .a12 (a12), .a22 (a22), .a32 (a32), .m11 (m11), .m12 (m12), .m21 (m21), .m22 (m22), .x21 (x21), .x22 (x22) ); // Forward declarations wire mbusread; wire ioread, iowrite; wire [3:0] io_in; reg [3:0] rom_out; reg chipsel; // Mux ROM and I/O data for output reg [3:0] data_out; always @(posedge sysclk) begin if (~clk2) begin data_out = 4'bxxxx; if (ioread) data_out = io_in; if (mbusread) data_out = rom_out; end end // Latch external bus drive signal wire n0108 = ((chipsel & (m11 | m21)) | ioread) & ~poc; reg extbusdrive; always @(posedge sysclk) begin if (~clk2) begin extbusdrive <= n0108; end end // Drive the tristate data bus assign data_pad = extbusdrive ? data_out : 4'bzzzz; // Common chip number match wire chipnum = (data_pad == ROM_NUMBER); // ======================================= // ROM interface // ======================================= // Latch the address reg [7:0] fetch_addr; always @(posedge sysclk) begin if (clk2) begin if (a12) fetch_addr[ 3:0] <= data_pad; if (a22) fetch_addr[ 7:4] <= data_pad; end end // Latch the chip select always @(posedge sysclk) begin if (a12) chipsel <= 1'b0; if (a32 & clk2) chipsel <= cmrom & chipnum; end reg n0128; always @(posedge sysclk) begin if (clk1) begin n0128 <= ioread; end end assign mbusread = ~(ioread | n0128); // Access the external Block RAM array assign rom_addr = extbusdrive ? {ROM_NUMBER, fetch_addr} : 12'h000; // // Mux the ROM data for output // // A real i4001 muxes the ROM ouputs on M11 and M21, depending on // inertial delays to provide the required 40ns hold time when // changing from the upper 4 bits to the lower 4 bits. // // Actual measurements show the FPGA emulation of the i4001 switches // its outputs 10-12ns after CLK2 goes low, while the re-created // instruction pointer board needs at least 120ns hold time. Oops. // // By changing this mux to use M12 and M22 instead, the outputs // are held at the correct values long enough for the IP board to // latch the correct values. // always @(posedge sysclk) begin if (m12) rom_out = rom_data[7:4]; if (m22) rom_out = rom_data[3:0]; end // ======================================= // I/O Port interface // ======================================= localparam [3:0] OPA_WRR = 4'b0010; localparam [3:0] OPA_RDR = 4'b1010; // SRC flip-flop reg srcff; always @(posedge sysclk) begin if (clk2) begin if (x22 & cmrom) srcff <= chipnum; end end wire m22_srcff_cm = m22 & srcff & cmrom; // Decode an I/O Read operation reg n0161; // always @(*) begin // if (a12) // n0161 <= 1'b0; // if (clk2 & m22_srcff_cm & (data_pad == OPA_RDR)) // n0161 <= 1'b1; // end always @(posedge sysclk) begin if (clk2) begin if (a12) n0161 <= 1'b0; if (m22_srcff_cm & (data_pad == OPA_RDR)) n0161 <= 1'b1; end end assign ioread = x21 & n0161; // Decode an I/O Write operation reg n0135; // always @(*) begin // if (a12) // n0135 <= 1'b0; // if (clk2 & m22_srcff_cm & (data_pad == OPA_WRR)) // n0135 <= 1'b1; // end always @(posedge sysclk) begin if (clk2) begin if (a12) n0135 <= 1'b0; if (m22_srcff_cm & (data_pad == OPA_WRR)) n0135 <= 1'b1; end end assign iowrite = x22 & n0135; // Latch new output data reg [3:0] io_out; always @(posedge sysclk) begin if (clear | poc) io_out <= 4'b0000; if (clk2 & iowrite) begin io_out <= data_pad; end end // I/O config generate genvar p; for (p = 0; p < 4; p = p + 1) begin: IO_PORTS if (IO_OUTPUT[p]) begin: IO_OUT_CONFIG if (IO_INVERT[p]) assign io_pad[p] = ~io_out[p]; else assign io_pad[p] = io_out[p]; end else begin: IO_IN_CONFIG assign io_pad[p] = 1'bz; if (IO_PULLUP[p]) PULLUP pu(.O(io_pad[p])); if (IO_PULLDOWN[p]) PULLDOWN pd(.O(io_pad[p])); end if (IO_INVERT[p]) assign io_in[p] = ~io_pad[p]; else assign io_in[p] = io_pad[p]; end endgenerate endmodule