URL
https://opencores.org/ocsvn/mcs-4/mcs-4/trunk
Subversion Repositories mcs-4
Compare Revisions
- This comparison shows the changes necessary to convert path
/mcs-4/trunk
- from Rev 5 to Rev 6
- ↔ Reverse comparison
Rev 5 → Rev 6
/rtl/verilog/common/clockgen.v
0,0 → 1,119
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 common clock generator sub-module |
// |
// This module implements a two-phase clock generator suitable |
// for use with the MCS-4 emulation. |
// |
// This file is part of the MCS-4 project hosted at OpenCores: |
// http://www.opencores.org/cores/mcs-4/ |
// |
// Copyright © 2012, 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 clockgen #( |
parameter SYSCLK_TCY = 20, // System clock cycle time in nanoseconds |
parameter EXT_CLK_PROP = 0 // External clock propagation delay in sysclk cycles |
) ( |
input wire sysclk, |
output reg clk1, |
output reg clk2, |
output reg clk1_ext, |
output reg clk2_ext |
); |
|
// |
// Instruction phase timing in nanoseconds |
// |
// These are chosen to be nicely divisible by the clock |
// period from any of these three systems while giving |
// timing that would be compatible with a real i4004: |
// * 50ns (20 MHz) -- P170-DH replacement board |
// * 20ns (50 MHz) -- Digilent Spartan-3e Starter board |
// * 10ns (100 MHz) -- Digilent Atlys Spartan-6 board |
// |
localparam TPW = 400; // Clock high pulse width |
localparam TD1 = 400; // Delay from clk1 to clk2 |
localparam TD2 = 200; // Delay from clk2 to clk1 |
|
// MCS-4 instruction phase cycle time |
localparam TCY = TD1 + TPW + TD2 + TPW; |
|
// Calculate counter maximum value and width |
localparam CMAX = (TCY / SYSCLK_TCY) - 1; |
localparam W = clog2(CMAX); |
|
// |
// Instruction phase timing in sysclk cycles |
// |
localparam SYSCLK_TPW = TPW / SYSCLK_TCY; |
localparam SYSCLK_TD1 = TD1 / SYSCLK_TCY; |
localparam SYSCLK_TD2 = TD2 / SYSCLK_TCY; |
|
// Divide the system clock to produce basic machine cycle |
localparam [W-1:0] CLOCKDIV_MAX = CMAX; |
reg [W-1:0] clockdiv = 1'd0; |
always @(posedge sysclk) begin |
clockdiv <= (clockdiv == CLOCKDIV_MAX) ? 1'd0 : (clockdiv + 1'd1); |
end |
|
// Set the timing of the internal 2-phase clocks |
localparam [W-1:0] CLK1_START = SYSCLK_TD2 - 1, |
CLK1_END = CLK1_START + SYSCLK_TPW, |
CLK2_START = CLK1_END + SYSCLK_TD1, |
CLK2_END = CLK2_START + SYSCLK_TPW; |
|
always @(posedge sysclk) begin |
case (clockdiv) |
CLK1_START: clk1 <= 1'b1; |
CLK1_END: clk1 <= 1'b0; |
endcase |
end |
|
always @(posedge sysclk) begin |
case (clockdiv) |
CLK2_START: clk2 <= 1'b1; |
CLK2_END: clk2 <= 1'b0; |
endcase |
end |
|
// |
// Set the timing of the external 2-phase clocks |
// |
// It takes ~70ns for the clocks to propagate from the FPGA through the |
// clock driver chip to the reconstructed i4004 CPU circuitry. To match |
// the timings, this module also provides advanced clock outputs. |
// |
localparam [W-1:0] CLK1_EXT_START = CLK1_START - EXT_CLK_PROP, |
CLK1_EXT_END = CLK1_END - EXT_CLK_PROP, |
CLK2_EXT_START = CLK2_START - EXT_CLK_PROP, |
CLK2_EXT_END = CLK2_END - EXT_CLK_PROP; |
|
always @(posedge sysclk) begin |
case (clockdiv) |
CLK1_EXT_START: clk1_ext <= 1'b1; |
CLK1_EXT_END: clk1_ext <= 1'b0; |
endcase |
end |
|
always @(posedge sysclk) begin |
case (clockdiv) |
CLK2_EXT_START: clk2_ext <= 1'b1; |
CLK2_EXT_END: clk2_ext <= 1'b0; |
endcase |
end |
|
`include "functions.vh" |
|
endmodule |
/rtl/verilog/common/clockgen_tb.v
0,0 → 1,65
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 common clock generator testbench |
// |
// This module is a testbench for the two-phase clock generator module. |
// |
// As a testbench, it leaves much to be desired. It allows a user to |
// observe the behavior of the module through simulation, but does |
// not verify its proper operation. Use at your own risk. |
// |
// This file is part of the MCS-4 project hosted at OpenCores: |
// http://www.opencores.org/cores/mcs-4/ |
// |
// Copyright © 2012, 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 clockgen_tb; |
|
parameter SYSCLK_TCY = 20; |
|
// Inputs |
reg sysclk; |
|
// Outputs |
wire clk1; |
wire clk2; |
wire clk1_ext; |
wire clk2_ext; |
|
// Instantiate the Unit Under Test (UUT) |
clockgen #( |
.SYSCLK_TCY (SYSCLK_TCY), |
.EXT_CLK_PROP (4) |
) clockgen ( |
.sysclk (sysclk), |
.clk1 (clk1), |
.clk2 (clk2), |
.clk1_ext (clk1_ext), |
.clk2_ext (clk2_ext) |
); |
|
always begin |
sysclk = 1'b0; |
#(SYSCLK_TCY / 2); |
sysclk = 1'b1; |
#(SYSCLK_TCY / 2); |
end |
|
initial begin |
$display ("counter width (W) is %d\n", clockgen.W); |
end |
|
endmodule |
|
/rtl/verilog/common/counter.v
1,18 → 1,21
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// 4004 Counter sub-module |
// |
// |
// MCS-4 common counter sub-module |
// |
// This module emulates the counter logic found in Intel MCS-4 |
// integrated circuits such as the i4004 CPU. |
// |
// This file is part of the MCS-4 project hosted at OpenCores: |
// http://www.opencores.org/cores/mcs-4/ |
// |
|
// |
// |
// Copyright © 2012, 2021 by Reece Pollack <rrpollack@opencores.org> |
// |
// These materials are provided under the Creative Commons |
// "Attribution-NonCommercial-ShareAlike" Public License. They |
// are NOT "public domain" and are protected by copyright. |
// |
// "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. |
19,17 → 22,34
// |
//////////////////////////////////////////////////////////////////////// |
|
module counter( |
input wire sysclk, |
input wire step_a, |
input wire step_b, |
output reg q = 1'b0 |
); |
module counter ( |
input wire sysclk, |
|
reg q_n = 1'b1; |
always @(posedge sysclk) begin |
if (step_a) q <= ~q_n; |
if (step_b) q_n <= q; |
end |
input wire step_a_in, |
input wire step_b_in, |
|
output wire step_a_out, |
output wire step_b_out, |
output wire q, |
output wire qn |
); |
|
reg master = 1'b0; |
reg slave = 1'b0; |
always @(posedge sysclk) begin |
if (step_a_in) |
master <= ~slave; |
end |
|
always @(posedge sysclk) begin |
if (step_b_in) |
slave <= master; |
end |
|
assign step_a_out = slave; |
assign step_b_out = ~slave; |
|
assign q = step_a_out; |
assign qn = step_b_out; |
|
endmodule |
/rtl/verilog/common/counter_tb.v
0,0 → 1,107
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 common counter testbench |
// |
// This module is a testbench for the counter logic found in Intel MCS-4 |
// integrated circuits such as the i4004 CPU. |
// |
// This file is part of the MCS-4 project hosted at OpenCores: |
// http://www.opencores.org/cores/mcs-4/ |
// |
// Copyright © 2012, 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 counter_tb; |
|
// Inputs |
reg sysclk; |
reg clk1 = 1'b0; |
reg clk2 = 1'b0; |
wire step; |
|
// Outputs |
wire q0, q0n, q1, q1n, q2, q2n; |
|
// Inter-module connections |
wire b01a, b01b; |
wire b12a, b12b; |
|
// Instantiate the Unit Under Test (UUT) |
counter b0 ( |
.step_a_in(clk1), |
.step_b_in(step), |
.step_a_out(b01a), |
.step_b_out(b01b), |
.q(q0), |
.qn(q0n) |
); |
|
counter b1 ( |
.step_a_in(b01a), |
.step_b_in(b01b), |
.step_a_out(b12a), |
.step_b_out(b12b), |
.q(q1), |
.qn(q1n) |
); |
|
counter b2 ( |
.step_a_in(b12a), |
.step_b_in(b12b), |
.step_a_out(), |
.step_b_out(), |
.q(q2), |
.qn(q2n) |
); |
|
always begin |
sysclk = 1'b0; #10; |
sysclk = 1'b1; #10; |
end |
|
reg [4:0] clockdiv = 5'b0; |
always @(posedge sysclk) begin |
clockdiv = (clockdiv == 5'd19) ? 5'b0 : (clockdiv + 5'd1); |
if (clockdiv == 5'd0 ) clk1 = 1'b1; |
if (clockdiv == 5'd5 ) clk1 = 1'b0; |
if (clockdiv == 5'd10) clk2 = 1'b1; |
if (clockdiv == 5'd15) clk2 = 1'b0; |
end |
|
// Generate the 8 execution phase indicators |
reg [0:7] master = 8'h00; |
reg [0:7] slave = 8'h00; |
reg sync_pad; |
always @(*) begin |
if (clk2) |
master <= {~|slave[0:6], slave[0:6]}; |
else |
sync_pad <= master[7]; |
|
if (clk1) |
slave <= master; |
end |
|
wire a12 = slave[0]; |
wire a22 = slave[1]; |
wire a32 = slave[2]; |
wire m12 = slave[3]; |
wire m22 = slave[4]; |
wire x12 = slave[5]; |
wire x22 = slave[6]; |
wire x32 = slave[7]; |
|
assign step = x32 & clk2; |
|
endmodule |
/rtl/verilog/common/functions.vh
0,0 → 1,68
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 common useful functions |
// |
// This file contains a set of useful functions used by other |
// MCS-4 emulation modules. |
// |
// 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. |
// |
//////////////////////////////////////////////////////////////////////// |
|
// |
// Calculate integer ceil(log2(value)) |
// |
// This is useful for determining the width of a register |
// required to hold the specified value. |
// |
// The implementation of $clog2() in iSim is FUBAR. This |
// performs the same function, except it actually works. |
// |
function integer clog2 ( |
input integer value |
); |
integer bits; |
begin |
value = value - 1; |
for (bits = 0; value > 0; bits = bits + 1) |
value = value >> 1; |
clog2 = bits; |
end |
endfunction |
|
// |
// Convert Nanoseconds to Cycles |
// |
// This converts nanoseconds to cycles, rounded up. This |
// implementation assumes SYSCLK_TCY is already defined. |
// |
function integer nstocy ( |
input integer ns |
); |
begin |
nstocy = (ns + (SYSCLK_TCY - 1)) / SYSCLK_TCY; |
end |
endfunction |
|
// |
// Calculate a Base-2 ceiling |
// |
function integer ceil2 ( |
input integer value, |
input integer step |
); |
begin |
ceil2 = (value + (step - 1)) & ~(step - 1); |
end |
endfunction |
/rtl/verilog/common/timing_generator.v
0,0 → 1,110
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 clock phase generator module |
// |
// 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. |
// |
//////////////////////////////////////////////////////////////////////// |
|
/* |
* Generate the clock phase timing internal to the i4004 CPU |
*/ |
module timing_generator ( |
input wire sysclk, // System clock |
input wire clk1, // Clock phase 1 |
input wire clk2, // Clock phase 2 |
|
output reg a11, |
output reg a12, |
output reg a21, |
output reg a22, |
output reg a31, |
output reg a32, |
|
output reg m11, |
output reg m12, |
output reg m21, |
output reg m22, |
|
output reg x11, |
output reg x12, |
output reg x21, |
output reg x22, |
output reg x31, |
output reg x32, |
|
output reg sync |
); |
|
|
// |
// Generate the cycle timing |
// |
always @(posedge sysclk) begin |
if (clk2) begin |
a11 <= ~(a12 | a22 | a32 | m12 | m22 | x12 | x22); |
a21 <= a12; |
a31 <= a22; |
m11 <= a32; |
m21 <= m12; |
x11 <= m22; |
x21 <= x12; |
x31 <= x22; |
end |
end |
|
always @(posedge sysclk) begin |
if (clk1) begin |
a12 <= a11; |
a22 <= a21; |
a32 <= a31; |
m12 <= m11; |
m22 <= m21; |
x12 <= x11; |
x22 <= x21; |
x32 <= x31; |
end |
end |
|
always @(posedge sysclk) begin |
if (~clk2) |
sync <= x31; |
end |
|
// This is self-initializing in hardware, but |
// initialization is required for simulation to |
// avoid endlessly propagating X states |
initial begin |
a11 = 1'b0; |
a12 = 1'b0; |
a21 = 1'b0; |
a22 = 1'b0; |
a31 = 1'b0; |
a32 = 1'b0; |
m11 = 1'b0; |
m12 = 1'b0; |
m21 = 1'b0; |
m22 = 1'b0; |
x11 = 1'b0; |
x12 = 1'b0; |
x21 = 1'b0; |
x22 = 1'b0; |
x31 = 1'b0; |
x32 = 1'b0; |
sync = 1'b0; |
end |
|
endmodule |
/rtl/verilog/common/timing_recovery.v
0,0 → 1,83
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 clock phase recovery module |
// |
// 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. |
// |
//////////////////////////////////////////////////////////////////////// |
|
/* |
* Recover the clock phase timing from the MCS-4 bus |
* |
* This module is for use in other component emulations |
* such as the i4001 ROM and the i4002 RAM chips. |
*/ |
module timing_recovery ( |
input wire sysclk, // System clock |
input wire clk1, // Clock phase 1 |
input wire clk2, // Clock phase 2 |
input wire sync, // Clock sync |
|
output reg a11, |
output reg a12, |
output reg a21, |
output reg a22, |
output reg a31, |
output reg a32, |
|
output reg m11, |
output reg m12, |
output reg m21, |
output reg m22, |
|
output reg x11, |
output reg x12, |
output reg x21, |
output reg x22, |
output reg x31, |
output reg x32 |
); |
|
// |
// Recover the cycle timing |
// |
always @(posedge sysclk) begin |
if (clk2) begin |
a11 <= sync; |
a21 <= a12; |
a31 <= a22; |
m11 <= a32; |
m21 <= m12; |
x11 <= m22; |
x21 <= x12; |
x31 <= x22; |
end |
end |
|
always @(posedge sysclk) begin |
if (clk1) begin |
a12 <= a11; |
a22 <= a21; |
a32 <= a31; |
m12 <= m11; |
m22 <= m21; |
x12 <= x11; |
x22 <= x21; |
x32 <= x31; |
end |
end |
|
endmodule |
/rtl/verilog/common/timing_tb.v
0,0 → 1,140
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 clock phase generator and recovery module testbench |
// |
// As a testbench, it leaves much to be desired. It allows a user to |
// observe the behavior of the modules through simulation, but does |
// not verify their proper operation. Use at your own risk. |
// |
// 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 timing_tb; |
|
localparam SYSCLK_TCY = 50; |
|
// Shared |
reg sysclk; |
wire sync; |
|
// Clockgen outputs |
wire clk1; |
wire clk2; |
|
clockgen #( |
.SYSCLK_TCY (SYSCLK_TCY) |
) clockgen ( |
.sysclk (sysclk), |
.clk1 (clk1), |
.clk2 (clk2) |
); |
|
// Generater outputs |
wire gen_a11; |
wire gen_a12; |
wire gen_a21; |
wire gen_a22; |
wire gen_a31; |
wire gen_a32; |
wire gen_m11; |
wire gen_m12; |
wire gen_m21; |
wire gen_m22; |
wire gen_x11; |
wire gen_x12; |
wire gen_x21; |
wire gen_x22; |
wire gen_x31; |
wire gen_x32; |
|
// Instantiate the timing generator |
timing_generator generator ( |
.sysclk(sysclk), |
.clk1(clk1), |
.clk2(clk2), |
.a11(gen_a11), |
.a12(gen_a12), |
.a21(gen_a21), |
.a22(gen_a22), |
.a31(gen_a31), |
.a32(gen_a32), |
.m11(gen_m11), |
.m12(gen_m12), |
.m21(gen_m21), |
.m22(gen_m22), |
.x11(gen_x11), |
.x12(gen_x12), |
.x21(gen_x21), |
.x22(gen_x22), |
.x31(gen_x31), |
.x32(gen_x32), |
.sync(sync) |
); |
|
// Recovery outputs |
wire rec_a11; |
wire rec_a12; |
wire rec_a21; |
wire rec_a22; |
wire rec_a31; |
wire rec_a32; |
wire rec_m11; |
wire rec_m12; |
wire rec_m21; |
wire rec_m22; |
wire rec_x11; |
wire rec_x12; |
wire rec_x21; |
wire rec_x22; |
wire rec_x31; |
wire rec_x32; |
|
// Instantiate the timing recovery |
timing_recovery recovery ( |
.sysclk(sysclk), |
.clk1(clk1), |
.clk2(clk2), |
.sync(sync), |
.a11(rec_a11), |
.a12(rec_a12), |
.a21(rec_a21), |
.a22(rec_a22), |
.a31(rec_a31), |
.a32(rec_a32), |
.m11(rec_m11), |
.m12(rec_m12), |
.m21(rec_m21), |
.m22(rec_m22), |
.x11(rec_x11), |
.x12(rec_x12), |
.x21(rec_x21), |
.x22(rec_x22), |
.x31(rec_x31), |
.x32(rec_x32) |
); |
|
initial begin |
// Initialize Inputs |
sysclk = 0; |
|
forever begin |
#(SYSCLK_TCY/2) sysclk = ~sysclk; |
end |
end |
|
endmodule |
|
/rtl/verilog/i4001/busicom.mem
0,0 → 1,1287
//////////////////////////////////////////// |
// busicode - Verilog memory list |
// created from ..\asm\busicode.hex by hex2code.pl |
// StarBoard Design |
|
@0 |
F0 |
11 |
01 |
50 |
B0 |
51 |
5F |
AD |
B1 |
F0 |
51 |
5F |
AD |
1C |
29 |
68 |
51 |
73 |
27 |
EC |
F5 |
B3 |
68 |
F0 |
51 |
A0 |
F3 |
B3 |
F5 |
E1 |
66 |
27 |
EA |
F5 |
F7 |
14 |
00 |
52 |
46 |
40 |
00 |
B0 |
EC |
F8 |
F8 |
E4 |
27 |
EA |
E7 |
50 |
64 |
27 |
EA |
E6 |
34 |
20 |
A0 |
A5 |
B1 |
30 |
68 |
51 |
73 |
D0 |
E1 |
D1 |
F3 |
F5 |
FC |
85 |
1A |
00 |
F0 |
00 |
00 |
11 |
4F |
50 |
B0 |
26 |
20 |
28 |
10 |
53 |
00 |
51 |
00 |
71 |
5A |
60 |
14 |
4B |
F7 |
14 |
57 |
43 |
02 |
D4 |
40 |
D4 |
D3 |
29 |
E2 |
D0 |
E2 |
C0 |
6C |
22 |
20 |
23 |
EA |
F6 |
73 |
6D |
1A |
76 |
F0 |
BC |
C0 |
A9 |
14 |
D9 |
28 |
00 |
F0 |
51 |
4A |
40 |
F7 |
BB |
C7 |
63 |
53 |
19 |
1A |
68 |
58 |
05 |
41 |
31 |
18 |
22 |
12 |
05 |
0C |
9D |
6D |
3D |
BD |
8D |
5D |
2D |
06 |
7D |
4D |
1D |
0D |
AD |
A4 |
0E |
BF |
06 |
91 |
98 |
F1 |
CD |
D7 |
FD |
8A |
05 |
61 |
F9 |
D7 |
D7 |
CA |
C5 |
50 |
6A |
28 |
07 |
50 |
64 |
79 |
B4 |
26 |
18 |
22 |
00 |
D1 |
50 |
65 |
27 |
EA |
FC |
B9 |
A2 |
F5 |
F7 |
1C |
77 |
A9 |
79 |
CD |
40 |
7A |
14 |
61 |
B2 |
F5 |
FA |
F6 |
B2 |
83 |
B3 |
D0 |
82 |
B2 |
50 |
64 |
77 |
BF |
29 |
A2 |
F5 |
F7 |
14 |
F8 |
EF |
F2 |
F7 |
1C |
F7 |
EC |
B9 |
29 |
A3 |
E0 |
69 |
29 |
E9 |
1C |
7A |
A2 |
E0 |
69 |
A9 |
E4 |
DF |
E7 |
28 |
00 |
26 |
10 |
19 |
FD |
C0 |
33 |
A5 |
F2 |
F2 |
86 |
B8 |
B6 |
41 |
0E |
66 |
66 |
66 |
66 |
66 |
27 |
E9 |
29 |
E0 |
69 |
77 |
0E |
27 |
EC |
B3 |
ED |
29 |
E5 |
B3 |
E4 |
C0 |
D4 |
85 |
B6 |
29 |
E9 |
27 |
EB |
FB |
E0 |
69 |
77 |
21 |
F1 |
C0 |
D4 |
85 |
B8 |
41 |
33 |
D4 |
85 |
B6 |
FA |
F9 |
29 |
E8 |
F1 |
27 |
EB |
FB |
E0 |
69 |
77 |
35 |
1A |
43 |
6D |
C1 |
68 |
68 |
68 |
68 |
68 |
68 |
29 |
E0 |
79 |
4A |
E4 |
E5 |
C0 |
68 |
68 |
29 |
E9 |
BD |
E0 |
79 |
53 |
C0 |
DE |
B9 |
AD |
68 |
68 |
BD |
A9 |
F8 |
F1 |
B9 |
29 |
E9 |
BD |
E0 |
A9 |
1C |
61 |
C0 |
68 |
68 |
29 |
EE |
F8 |
F3 |
C1 |
68 |
68 |
68 |
68 |
29 |
EE |
F6 |
C1 |
27 |
EE |
F5 |
C1 |
66 |
66 |
66 |
27 |
E6 |
C0 |
66 |
66 |
D1 |
41 |
81 |
D8 |
41 |
81 |
A4 |
41 |
82 |
27 |
EE |
F5 |
FA |
F6 |
E6 |
C0 |
D4 |
85 |
B8 |
DE |
B9 |
41 |
A2 |
DE |
B9 |
BD |
68 |
29 |
DF |
EB |
79 |
A2 |
F3 |
C1 |
AD |
FB |
C1 |
AD |
F8 |
BD |
F3 |
C1 |
D7 |
95 |
C1 |
DC |
84 |
C1 |
A5 |
F6 |
B5 |
F3 |
C1 |
A4 |
F6 |
C1 |
D4 |
85 |
B8 |
29 |
AB |
E5 |
C0 |
29 |
ED |
BB |
C0 |
7B |
CD |
6A |
C0 |
DD |
9B |
F3 |
D0 |
9A |
C1 |
42 |
D3 |
00 |
42 |
94 |
42 |
A3 |
42 |
AA |
42 |
AE |
42 |
B3 |
42 |
B9 |
42 |
CA |
42 |
DE |
42 |
E7 |
42 |
EC |
42 |
46 |
44 |
00 |
51 |
80 |
51 |
81 |
51 |
81 |
2A |
00 |
40 |
00 |
6F |
6F |
6F |
6F |
6F |
6F |
BF |
F4 |
BF |
7F |
10 |
DA |
51 |
4A |
2E |
FF |
BA |
DF |
B9 |
29 |
E0 |
42 |
2C |
7F |
17 |
DF |
BF |
A5 |
42 |
26 |
D1 |
8F |
F7 |
14 |
25 |
A4 |
BF |
BE |
F6 |
F3 |
DE |
F6 |
42 |
26 |
A4 |
BE |
29 |
ED |
BA |
ED |
BB |
11 |
2C |
D2 |
BD |
EC |
F6 |
F7 |
E1 |
50 |
B0 |
68 |
6B |
AB |
B9 |
51 |
A2 |
F7 |
14 |
37 |
11 |
3F |
F0 |
E1 |
E2 |
7D |
53 |
2A |
0C |
2E |
00 |
D8 |
11 |
4B |
E1 |
50 |
B0 |
7B |
4B |
C0 |
50 |
6A |
B8 |
DD |
9F |
F1 |
1C |
5F |
BA |
DF |
42 |
61 |
27 |
E9 |
77 |
77 |
AA |
1C |
68 |
52 |
8F |
AF |
52 |
8A |
AE |
52 |
8A |
19 |
6E |
D2 |
29 |
E1 |
50 |
B2 |
42 |
3F |
52 |
8A |
AA |
14 |
83 |
97 |
F1 |
1C |
83 |
DA |
52 |
8A |
A7 |
9B |
F7 |
14 |
56 |
42 |
5C |
9C |
F1 |
1C |
8F |
FA |
D1 |
F5 |
F5 |
40 |
65 |
A4 |
BD |
26 |
40 |
27 |
EE |
1C |
A1 |
D8 |
E6 |
F0 |
51 |
4A |
41 |
C6 |
51 |
0A |
51 |
46 |
51 |
49 |
C0 |
D3 |
B5 |
FA |
C1 |
D1 |
B3 |
BB |
42 |
C2 |
52 |
F9 |
EF |
E5 |
42 |
C2 |
52 |
F9 |
DD |
9B |
F1 |
82 |
BA |
F7 |
BA |
93 |
BB |
F3 |
BA |
99 |
BA |
F1 |
C0 |
52 |
F9 |
A3 |
8B |
82 |
BB |
F7 |
BA |
C0 |
D4 |
85 |
B6 |
27 |
EC |
B2 |
29 |
EC |
82 |
F6 |
C1 |
52 |
D6 |
F7 |
66 |
27 |
E4 |
DF |
BD |
C0 |
29 |
EC |
F4 |
E4 |
C0 |
DB |
8D |
1A |
F1 |
6E |
D0 |
29 |
EB |
FB |
E0 |
79 |
F1 |
C0 |
27 |
ED |
B2 |
29 |
ED |
B3 |
C0 |
32 |
C0 |
30 |
40 |
4B |
ED |
6C |
14 |
75 |
0E |
D9 |
FC |
A7 |
0F |
FB |
8D |
04 |
02 |
87 |
EF |
FC |
6D |
0F |
7B |
0F |
76 |
46 |
8D |
A2 |
3C |
48 |
A0 |
73 |
E1 |
9E |
32 |
9A |
36 |
E5 |
51 |
51 |
34 |
29 |
21 |
51 |
A9 |
3F |
52 |
A7 |
29 |
52 |
CA |
A7 |
22 |
53 |
CF |
3C |
DD |
A7 |
24 |
FF |
85 |
F1 |
5D |
CE |
73 |
DD |
5D |
A7 |
40 |
8D |
03 |
E3 |
E5 |
0E |
49 |
52 |
5E |
5A |
A9 |
56 |
AC |
4D |
21 |
A7 |
51 |
A0 |
40 |
CF |
3C |
5E |
5A |
DD |
A7 |
56 |
86 |
F3 |
FE |
CA |
CA |
A7 |
67 |
FE |
7B |
6F |
90 |
76 |
47 |
02 |
A7 |
1C |
04 |
0C |
A7 |
6A |
0D |
C2 |
B1 |
10 |
B4 |
7B |
6E |
9E |
DF |
CF |
9A |
CE |
84 |
CA |
5F |
A7 |
7C |
DD |
53 |
9A |
7C |
A7 |
3C |
6C |
66 |
75 |
66 |
D9 |
A7 |
98 |
6C |
98 |
75 |
97 |
A7 |
98 |
82 |
AE |
7B |
B1 |
AA |
77 |
A3 |
FD |
EB |
B4 |
A8 |
F1 |
E9 |
9A |
9E |
A7 |
3C |
DB |
AB |
FC |
C6 |
03 |
BC |
B0 |
E7 |
D4 |
B7 |
1E |
97 |
BD |
31 |
3C |
31 |
BD |
1E |
2C |
BC |
01 |
0D |
BF |
B4 |
FF |
B7 |
AC |
8A |
EF |
82 |
49 |
D9 |
4A |
FC |
4A |
7F |
F1 |
6C |
D5 |
75 |
D3 |
0B |
46 |
FC |
EF |
FD |
F1 |
D7 |
A9 |
DF |
53 |
9A |
E3 |
5F |
F3 |
BC |
5F |
E7 |
F3 |
74 |
E8 |
A7 |
EE |
00 |
CA |
CE |
ED |
DD |
5F |
C2 |
B7 |
DA |
F3 |
FD |
02 |
0E |
03 |
0C |
04 |
0D |
F1 |
09 |
FA |
44 |
F1 |
09 |
FA |
F1 |
20 |
28 |
11 |
06 |
50 |
B0 |
26 |
20 |
28 |
10 |
32 |
F0 |
54 |
50 |
71 |
11 |
60 |
14 |
02 |
F7 |
14 |
0E |
30 |
44 |
02 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
51 |
A7 |
53 |
61 |
3E |
65 |
63 |
44 |
9C |
5B |
55 |
6A |
36 |
58 |
7A |
5D |
41 |
5F |
85 |
57 |
98 |
35 |
A9 |
5B |
9F |
7A |
96 |
36 |
59 |
93 |
2E |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
33 |
41 |
FE |
41 |
48 |
41 |
4A |
68 |
68 |
41 |
53 |
41 |
04 |
41 |
34 |
41 |
21 |
41 |
A2 |
41 |
9A |
BD |
29 |
ED |
BB |
C0 |
2E |
6D |
AB |
B7 |
BA |
F6 |
AB |
F6 |
8E |
BB |
F7 |
BA |
B7 |
F6 |
F3 |
C1 |
AF |
B9 |
FA |
D0 |
29 |
EB |
FB |
E0 |
79 |
7D |
C0 |
AF |
B9 |
F3 |
29 |
E9 |
97 |
12 |
8E |
D9 |
E0 |
79 |
87 |
F0 |
C0 |
7B |
96 |
6A |
FA |
C1 |
AF |
F8 |
BF |
C1 |
41 |
5F |
00 |
27 |
E6 |
20 |
40 |
26 |
00 |
40 |
4B |
41 |
02 |
41 |
04 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
// End of file |
/rtl/verilog/i4001/i4001.v
0,0 → 1,240
`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 |
/rtl/verilog/i4001/i4001_rom.v
0,0 → 1,52
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 i4001 shared ROM storage |
// |
// This module emulates the Intel 4001 ROM storage. It is separate from |
// the "i4001" module to make efficient use of FPGA block ram resources. |
// One i4001_rom instantiation 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_rom #( |
parameter ROM_FILE = "i4001-0.mem", |
parameter [3:0] ROM_NUMBER = 4'd0, |
parameter ROM_SIZE = 2048 |
) ( |
input wire sysclk, |
input wire [11:0] rom_addr, |
output reg [ 7:0] rom_data |
); |
|
(* rom_style="block" *) |
reg [7:0] rom_array [0:ROM_SIZE-1]; |
initial begin |
$readmemh (ROM_FILE, rom_array); |
end |
|
wire [11:0] array_addr = {rom_addr[11:8] - ROM_NUMBER, |
rom_addr[ 7:0]}; |
|
// Block RAMs have synchronous read ports |
// that require a clock to read the array |
always @(posedge sysclk) begin |
rom_data <= rom_array[array_addr]; |
end |
|
endmodule |
/rtl/verilog/i4001/i4001_tb.v
0,0 → 1,214
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 i4001 ROM testbench |
// |
// This module is a testbench for the i4001 and i4001_rom modules. |
// |
// This testbench instantiates i4001 and i4001_rom modules, and |
// and enough of the i4004 system bus logic to be able to test |
// ROM access. The testbench dumps the contents of ROM in a |
// textual hex dump format that can be reviewed by a human; not |
// the ideal testbench operation but useful at the time. |
// |
// 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_tb; |
|
localparam SYSCLK_TCY = 50; // 20 MHz Oscillator period in ns |
// localparam SYSCLK_TCY = 20; // 50 MHz Oscillator period in ns |
|
// Inputs |
reg sysclk; |
wire clk1; |
wire clk2; |
reg poc; |
reg clear_pad = 1'b0; |
|
wire [11:0] rom_addr; |
wire [ 7:0] rom_data; |
|
// Bidirs |
wire [3:0] data_pad; |
wire [3:0] io_pad; |
|
// Generate a system clock |
always begin |
sysclk = 1'b0; |
#(SYSCLK_TCY / 2); |
sysclk = 1'b1; |
#(SYSCLK_TCY / 2); |
end |
|
// Instantiate the 2-phase clock generator |
clockgen #( |
.SYSCLK_TCY (SYSCLK_TCY) |
) clockgen ( |
.sysclk (sysclk), |
.clk1 (clk1), |
.clk2 (clk2) |
); |
|
// Timing generator outputs |
wire a12; |
wire a22; |
wire a32; |
wire m12; |
wire m22; |
wire x12; |
wire x22; |
wire x32; |
wire sync; |
|
// Generate the 8 execution phase indicators |
timing_generator timing_generator ( |
.clk1 (clk1), |
.clk2 (clk2), |
.a12 (a12), |
.a22 (a22), |
.a32 (a32), |
.m12 (m12), |
.m22 (m22), |
.x12 (x12), |
.x22 (x22), |
.x32 (x32), |
.sync (sync) |
); |
|
reg [3:0] opr, opa; |
always @(*) begin |
if (clk2) begin |
if (m12) opr <= data_pad; |
if (m22) opa <= data_pad; |
end |
end |
|
// Manage the CPU tristate buffers |
reg ior = 1'b0; |
reg L; |
always @(*) begin |
if (clk2) |
L <= a32 | m12 | (x12 & (ior | poc)); |
end |
|
wire n0702 = ~clk2; |
reg n0707; |
always @(*) begin |
if (clk1) begin |
n0707 <= L; |
end |
end |
wire n0700 = n0707 | (L & n0702) | poc; |
|
|
reg [11:0] addr; |
reg [ 3:0] data_out; |
always @(*) begin |
if (poc) |
data_out = 4'bzzzz; |
else begin |
(* PARALLEL_CASE *) |
case (1'b1) |
a12: data_out = addr[ 3:0]; |
a22: data_out = addr[ 7:4]; |
a32: data_out = addr[11:8]; |
m12: data_out = 4'bxxxx; |
m22: data_out = 4'bxxxx; |
x12: data_out = opa; |
x22: data_out = 4'b1111; |
x32: data_out = 4'b1111; |
default: data_out = 4'bxxxx; |
endcase |
end |
end |
assign data_pad = n0700 ? 4'bzzzz : data_out; |
|
// Instantiate the Units Under Test (UUT) |
i4001 #( |
.ROM_NUMBER (4'd0) |
) rom_0 ( |
.sysclk (sysclk), |
.clk1_pad (clk1), |
.clk2_pad (clk2), |
.sync_pad (sync), |
.poc_pad (poc), |
.cmrom_pad (a32), |
.data_pad (data_pad), |
.io_pad (io_pad), |
.clear_pad (clear_pad), |
.rom_addr (rom_addr), |
.rom_data (rom_data) |
); |
|
i4001 #( |
.ROM_NUMBER (4'd1) |
) rom_1 ( |
.sysclk (sysclk), |
.clk1_pad (clk1), |
.clk2_pad (clk2), |
.sync_pad (sync), |
.poc_pad (poc), |
.cmrom_pad (a32), |
.data_pad (data_pad), |
.io_pad (io_pad), |
.clear_pad (clear_pad), |
.rom_addr (rom_addr), |
.rom_data (rom_data) |
); |
|
i4001_rom #( |
.ROM_FILE ("busicom.mem"), |
.ROM_NUMBER (4'd0) |
) rom_store ( |
.sysclk (sysclk), |
.rom_addr (rom_addr), |
.rom_data (rom_data) |
); |
|
|
always @(posedge sync) begin |
if (poc) |
addr <= 12'h000; |
else begin |
if (addr[3:0] == 4'h0) |
$write("\n@%03x", addr); |
$write(" %x%x", opr, opa); |
if (addr == 1279) begin |
$write("\n"); |
$finish; |
end |
addr <= addr + 1'b1; |
end |
end |
|
initial begin |
// Initialize Inputs |
poc = 1'b1; |
clear_pad = 1'b1; |
|
// Wait 3 SYNCs to reset |
@(posedge sync); |
@(posedge sync); |
@(posedge sync); |
#10; |
poc = 1'b0; |
clear_pad = 1'b0; |
|
end |
|
endmodule |
|
/rtl/verilog/i4002/i4002.v
0,0 → 1,266
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 i40021 RAM and Output Port module |
// |
// This module emulates the Intel 4002 RAM and Output Port chip. The |
// RAM storage is implemented using an "i4002_ram" module, which is |
// dependent on the availability of dual-port distributed RAM. |
// |
// 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 i4002 #( |
parameter CHIP_NUMBER = 0, // Mask and P0 config |
parameter RAM_ARRAY_SIZE = 32 // Size of the RAM array |
) ( |
input wire sysclk, // 20 MHz oscillator input |
// MCS-4 system bus interface |
input wire clk1, // MCS-4 Phase 1 clock |
input wire clk2, // MCS-4 Phase 2 clock |
input wire sync, // MCS-4 Phase synchronization |
input wire reset, // MCS-4 Synchronous reset |
input wire cm, // MCS-4 Command Line (bank select) |
inout tri [3:0] data, // MCS-4 bidirectional data bus |
|
output reg [3:0] oport, // i4002 Output port |
|
// Ram array dual-port interface |
input wire [4:0] ram0_addr2, |
output wire [3:0] ram0_data2_out, |
input wire [4:0] ram1_addr2, |
output wire [3:0] ram1_data2_out, |
input wire [4:0] ram2_addr2, |
output wire [3:0] ram2_data2_out, |
input wire [4:0] ram3_addr2, |
output wire [3:0] ram3_data2_out |
); |
|
// |
// Recover the cycle timing |
// |
wire a12, m12, m22, x22, x32; |
timing_recovery timing_recovery ( |
.sysclk (sysclk), |
.clk1 (clk1), |
.clk2 (clk2), |
.sync (sync), |
.a12 (a12), |
.m12 (m12), |
.m22 (m22), |
.x22 (x22), |
.x32 (x32) |
); |
|
// Capture OPA during the M2 subcycle |
reg io; |
reg [3:0] opa; |
always @(posedge sysclk) begin |
if (reset) begin |
io = 1'b0; |
opa = 4'b0000; |
end |
else begin |
if (clk2 & m22) begin |
io = cm; |
opa = data; |
end |
end |
end |
|
// Decode I/O type operations |
wire wrm = io & (opa == 4'b0000); |
wire wmp = io & (opa == 4'b0001); |
wire wrx = io & (opa[3:2] == 2'b01); |
wire rdm = io & (opa[3:2] == 2'b10) & (opa[1:0] != 2'b10); |
wire rdx = io & (opa[3:2] == 2'b11); |
|
// Capture the SRC address during the X22/X32 subcycles |
reg ram_sel = 1'b0; |
reg src_ram_sel = 1'b0; |
reg [1:0] reg_num = 2'b00; |
reg [3:0] char_num = 4'b0000; |
always @(posedge sysclk) begin |
if (reset) begin |
ram_sel = 1'b0; |
src_ram_sel = 1'b0; |
reg_num = 2'b00; |
char_num = 4'b0000; |
end |
else begin |
if (cm & x22 & clk2) begin |
ram_sel = (data[3:2] == CHIP_NUMBER); |
src_ram_sel = ram_sel; |
reg_num = data[1:0]; |
end |
if (clk2 & x32 & src_ram_sel) begin |
char_num = data; |
end |
if (a12) begin |
src_ram_sel = 1'b0; |
end |
end |
end |
|
// Decode the register address |
wire [4:0] reg_addr = opa[2] ? {3'b100, opa[1:0]} : |
{1'b0, char_num}; |
wire reg_write = ram_sel & clk2 & x22 & (wrm | wrx); |
wire reg0_write = reg_write & (reg_num == 2'b00); |
wire reg1_write = reg_write & (reg_num == 2'b01); |
wire reg2_write = reg_write & (reg_num == 2'b10); |
wire reg3_write = reg_write & (reg_num == 2'b11); |
|
// Latch the output port value |
always @(posedge sysclk) begin |
if (reset) begin |
oport = 4'b0000; |
end |
else if (ram_sel) begin |
if (clk2 & x22 & wmp) |
oport = data; |
end |
end |
|
// |
// In a real i4002, the RAM array is refreshed as follows: |
// 1) During the M11 subcycle, CLK1 causes all column sense |
// lines to be precharged to a "high" state. |
// 2) During the M12 subcycle, the refresh row counter selects |
// a row to be refreshed. CLK2 causes the selected row to |
// be read onto the column sense lines. |
// 3) During the M22 subcycle, the selected row is rewritten |
// with the data read during the M12 subcycle. |
// |
// The refresh row counter is 5 bits wide, and counts from 0x1f |
// down to 0x00 before rolling over. The upper bit determines |
// whether a "main memory" or "status" row is selected. Since |
// there are 16 "main memory" rows but only four status rows, |
// the status rows are refreshed four times per refresh cycle. |
// |
// The RAM array is written using a similar sequence: |
// 1) During the X11 subcycle, CLK1 causes all column sense |
// lines to be precharged to a "high" state. |
// 2) During the X12 subcycle, a row is selected based on the |
// most recent SRC command or the low two bits of OPA for |
// status register reads and writes. CLK2 causes the |
// selected row to be read onto the column sense lines. |
// 3) During the X21 subcycle, if the current operation is a |
// read, the data from the selected register is gated onto |
// the data bus. |
// 4) During the X22 subcycle, if the current operation is a |
// write, the selected row is written. The previously |
// selected register receives the data from the data bus, |
// while the other registers in the row receive the data |
// read during the X12 subcycle. |
// |
// When the RESET line is asserted, the RAM row read operations |
// are inhibited. This causes the refresh operations to write |
// zeros into the RAM. Also inhibited are the data bus gate |
// signals, preventing data bus values from being written during |
// any erroneous write operations. |
// |
reg [4:0] rfsh_addr = 5'd0; |
reg [4:0] rfsh_next = 5'd0; |
always @(posedge sysclk) begin |
if (m12) |
rfsh_addr <= rfsh_next; |
if (m22) |
rfsh_next <= rfsh_addr + 1'd1; |
end |
|
// |
// Mux the RAM's write port signals |
// |
wire [4:0] ram_addr = reset ? rfsh_addr : reg_addr; |
wire [3:0] ram_data_out = reset ? 4'h0 : data; |
wire ram0_write = reset ? 1'b1 : reg0_write; |
wire ram1_write = reset ? 1'b1 : reg1_write; |
wire ram2_write = reset ? 1'b1 : reg2_write; |
wire ram3_write = reset ? 1'b1 : reg3_write; |
|
// Select the correct RAM output |
wire [3:0] ram0_data_in; |
wire [3:0] ram1_data_in; |
wire [3:0] ram2_data_in; |
wire [3:0] ram3_data_in; |
reg [3:0] reg_data_in; |
always @(*) begin |
case (reg_num) |
2'b00 : reg_data_in = ram0_data_in; |
2'b01 : reg_data_in = ram1_data_in; |
2'b10 : reg_data_in = ram2_data_in; |
2'b11 : reg_data_in = ram3_data_in; |
endcase |
end |
|
wire reg_read = ram_sel & x22 & (rdm | rdx); |
assign data = reg_read ? reg_data_in : 4'bzzzz; |
|
|
// Instantiate RAM0 array |
i4002_ram #( |
.RAM_ARRAY_SIZE (RAM_ARRAY_SIZE) |
) ram0 ( |
.sysclk (sysclk), |
.addr (ram_addr), |
.write (ram0_write), |
.data_in (ram_data_out), |
.data_out (ram0_data_in), |
.addr2 (ram0_addr2), |
.data2_out (ram0_data2_out) |
); |
|
// Instantiate RAM1 array |
i4002_ram #( |
.RAM_ARRAY_SIZE (RAM_ARRAY_SIZE) |
) ram1 ( |
.sysclk (sysclk), |
.addr (ram_addr), |
.write (ram1_write), |
.data_in (ram_data_out), |
.data_out (ram1_data_in), |
.addr2 (ram1_addr2), |
.data2_out (ram1_data2_out) |
); |
|
// Instantiate RAM2 array |
i4002_ram #( |
.RAM_ARRAY_SIZE (RAM_ARRAY_SIZE) |
) ram2 ( |
.sysclk (sysclk), |
.addr (ram_addr), |
.write (ram2_write), |
.data_in (ram_data_out), |
.data_out (ram2_data_in), |
.addr2 (ram2_addr2), |
.data2_out (ram2_data2_out) |
); |
|
// Instantiate RAM3 array |
i4002_ram #( |
.RAM_ARRAY_SIZE (RAM_ARRAY_SIZE) |
) ram3 ( |
.sysclk (sysclk), |
.addr (ram_addr), |
.write (ram3_write), |
.data_in (ram_data_out), |
.data_out (ram3_data_in), |
.addr2 (ram3_addr2), |
.data2_out (ram3_data2_out) |
); |
|
endmodule |
/rtl/verilog/i4002/i4002_ram.v
0,0 → 1,74
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 i4002 RAM storage |
// |
// This module defines the RAM allocated to a single i4002 register, |
// containing 16x4-bit "main" memory array and a 4x4-bit "status" |
// array. An i4002 RAM chip contains four of these RAM register modules. |
// |
// This implementation allocates a single 20x4-bit dual-port |
// RAM: ram_array. The "main" memory array is represented by the |
// elements [0:15] of ram_array, while the "status" array is |
// represented by elements [16:19]. |
// |
// This module defines a dual-port array to allow the VFD driver access |
// to the "Working Register", WR, which is stored in RAM0 register 1. |
// The synthesis tools should recognize theinstantiations that do not |
// need to be dual-port and trim the unneeded logic and storage. |
// |
// 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 i4002_ram #( |
parameter RAM_ARRAY_SIZE = 32 // Size of the RAM array |
) ( |
input wire sysclk, |
input wire [4:0] addr, // Address |
input wire write, // Write Enable |
input wire [3:0] data_in, // Data input to write |
output wire [3:0] data_out, // Data output (unregistered) |
|
input wire [4:0] addr2, // 2nd port address |
output wire [3:0] data2_out // 2nd port data output (unregisted) |
); |
|
// |
// Infer a 32x4 distributed dual-port RAM |
// |
// The "status" characters are stored in [16:19] |
// |
(* ram_style="distributed" *) |
reg [3:0] ram_array [0:(RAM_ARRAY_SIZE-1)]; |
always @(posedge sysclk) begin |
if (write) begin |
ram_array[addr] <= data_in; |
end |
end |
assign data_out = ram_array[addr]; |
assign data2_out = ram_array[addr2]; |
|
`ifdef XILINX_ISIM |
// Pre-initialize the RAM |
genvar i; |
generate |
for (i = 0; i < RAM_ARRAY_SIZE; i = i + 1) begin : initial_ram |
initial ram_array[i] = 4'bxxxx; |
end |
endgenerate |
`endif |
|
endmodule |
/rtl/verilog/i4002/i4002_tb.v
0,0 → 1,269
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 i4001 RAM testbench |
// |
// This module is a testbench for the i4002 and i4002_ram modules. |
// |
// This testbench instantiates an i4002 RAM module, and enough of |
// the i4004 system bus logic to be able to test RAM access. |
// |
// 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 i4002_tb; |
|
localparam SYSCLK_TCY = 20; // sysclk period in nanoseconds |
|
// Inputs |
reg rst; |
reg reset; |
reg cm; |
reg [4:0] ram0_addr2; |
reg [4:0] ram1_addr2; |
reg [4:0] ram2_addr2; |
reg [4:0] ram3_addr2; |
|
// Outputs |
wire [3:0] oport; |
wire [3:0] ram0_data2_out; |
wire [3:0] ram1_data2_out; |
wire [3:0] ram2_data2_out; |
wire [3:0] ram3_data2_out; |
|
// Bidirs |
wire [3:0] data; |
|
// Simulate the system clock |
reg sysclk; |
always begin |
sysclk = 1'b0; |
#(SYSCLK_TCY / 2); |
sysclk = 1'b1; |
#(SYSCLK_TCY / 2); |
end |
|
// Instantiate a 2-phase clock generator |
wire clk1, clk2; |
clockgen #( |
.SYSCLK_TCY (SYSCLK_TCY) |
) clockgen ( |
.sysclk (sysclk), |
.clk1 (clk1), |
.clk2 (clk2) |
); |
|
// Generate the 8 execution phase indicators |
wire a12, a22, a32, m12, m22, x12, x22, x32, sync; |
timing_generator timing_generator ( |
.clk1 (clk1), |
.clk2 (clk2), |
.a12 (a12), |
.a22 (a22), |
.a32 (a32), |
.m12 (m12), |
.m22 (m22), |
.x12 (x12), |
.x22 (x22), |
.x32 (x32), |
.sync (sync) |
); |
|
|
// Instantiate the Unit Under Test (UUT) |
i4002 uut ( |
.sysclk (sysclk), |
.clk1 (clk1), |
.clk2 (clk2), |
.sync (sync), |
.reset (reset), |
.cm (cm), |
.data (data), |
.oport (oport), |
.ram0_addr2 (ram0_addr2), |
.ram0_data2_out (ram0_data2_out), |
.ram1_addr2 (ram1_addr2), |
.ram1_data2_out (ram1_data2_out), |
.ram2_addr2 (ram2_addr2), |
.ram2_data2_out (ram2_data2_out), |
.ram3_addr2 (ram3_addr2), |
.ram3_data2_out (ram3_data2_out) |
); |
|
reg data_dir = 1'b0; |
reg [3:0] data_out = 4'bxxxx; |
reg [3:0] data_in = 4'bxxxx; |
assign data = data_dir ? data_out : 4'bzzzz; |
|
initial begin |
// Initialize Inputs |
rst = 1; |
reset = 1; |
cm = 0; |
ram0_addr2 = 5'h00; |
ram1_addr2 = 5'h00; |
ram2_addr2 = 5'h00; |
ram3_addr2 = 5'h00; |
|
// Wait 100 ns for global reset to finish |
#100; |
rst = 0; |
@(negedge sync); |
reset = 0; |
end |
|
// Simulate enough of a i4004 CPU to access RAM |
always @(*) begin |
data_dir = 1'b1; |
data_out = 4'bxxxx; |
cm = 1'b0; |
case (1'b1) |
a12 : task_a12; |
a22 : task_a22; |
a32 : task_a32; |
m12 : task_m12; |
m22 : task_m22; |
x12 : task_x12; |
x22 : task_x22; |
x32 : task_x32; |
default : /* default */; |
endcase |
end |
|
// |
// i4004 simulation registers |
// |
// |
reg [11:0] ip = 12'h000; |
reg [1:0] chip_num = 2'b00; |
reg [1:0] reg_num = 2'b01; |
reg [3:0] char_num = 4'ha; |
reg [3:0] acc; |
|
reg [7:0] rom[0:'hfff]; |
wire [7:0] rom_data = rom[ip]; |
initial begin |
`ifdef USE_READMEMH |
$readmemh("i4002_tb.mem", rom, 0); |
`else |
rom[ 0] = 8'hFD; // DCL |
rom[ 1] = 8'h21; // SRC R0 |
rom[ 2] = 8'hE9; // RDM |
rom[ 3] = 8'hE0; // WRM |
rom[ 4] = 8'h21; // SRC R0 |
rom[ 5] = 8'hE9; // RDM |
rom[ 6] = 8'hE0; // WRM |
rom[ 7] = 8'hFF; // HLT (sim only) |
`endif |
end |
|
// Capture OPR during the M1 phase |
reg [3:0] opr; |
always @(*) begin |
if (clk2 & m12) |
opr = data; |
end |
|
// Capture OPA during the M2 phase |
reg [3:0] opa; |
always @(*) begin |
if (clk2 & m22) |
opa = data; |
end |
|
// Instruction decode |
wire src = (opr == 4'b0010) & opa[0]; |
wire io = (opr == 4'b1110); |
wire ior = io & opa[3]; |
wire iow = io & ~opa[3]; |
wire ag = (opr == 4'b1111); |
wire iac = ag & (opa == 4'b0010); |
|
always @(*) begin |
if (clk2 & ior) begin |
acc = data; |
end |
end |
|
task task_a12(); |
begin |
data_out = ip[3:0]; |
data_dir = 1'b1; |
end |
endtask : task_a12 |
|
task task_a22(); |
begin |
data_out = ip[7:4]; |
end |
endtask : task_a22 |
|
task task_a32(); |
begin |
data_out = ip[11:8]; |
cm = 1'b1; |
end |
endtask : task_a32 |
|
task task_m12(); |
begin |
data_out = rom_data[7:4]; |
end |
endtask : task_m12 |
|
task task_m22(); |
begin |
data_out = rom_data[3:0]; |
cm = io; |
if (~reset) begin |
ip = ip + 'd1; |
end |
end |
endtask : task_m22 |
|
task task_x12(); |
begin |
data_out = opa; |
if ({opr, opa} == 8'hFF) |
$stop(); |
end |
endtask : task_x12 |
|
task task_x22(); |
begin |
if (src) begin |
data_out = {chip_num, reg_num}; |
cm = 1'b1; |
end |
if (iow) begin |
data_out = acc + 'd1; |
end |
if (ior) begin |
data_dir = 1'b0; |
end |
end |
endtask : task_x22 |
|
task task_x32(); |
begin |
if (src) begin |
data_out = char_num; |
end |
end |
endtask : task_x32 |
|
|
endmodule |
/rtl/verilog/i4003/i4003.v
0,0 → 1,79
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 i4003 Shift Register |
// |
// This module emulates the Intel 4003 shift register chip. |
// |
// 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 i4003 #( |
parameter SYSCLK_TCY = 20 // System clock period in nanoseconds |
) ( |
input wire sysclk, |
input wire cp, |
input wire serial_in, |
input wire enable, |
output wire [9:0] parallel_out, |
output reg serial_out |
); |
|
initial begin |
serial_out = 1'b0; |
end |
|
localparam LATCH_DELAY_NS = 250; |
localparam LATCH_DELAY_CY = nstocy(LATCH_DELAY_NS); |
|
localparam W = clog2(LATCH_DELAY_CY); |
|
reg cp_delayed = 1'b0; |
reg [W-1:0] cp_delay = 0; |
wire cp_edge = (cp_delay == LATCH_DELAY_CY[W-1:0]); |
always @(posedge sysclk) begin |
if (cp == cp_delayed) begin |
cp_delay <= 0; |
end |
else begin |
if (cp_edge) begin |
cp_delay <= 0; |
cp_delayed <= cp; |
end |
else begin |
cp_delay <= cp_delay + 1'b1; |
end |
end |
end |
|
reg [9:0] shifter = 10'h000; |
always @(posedge sysclk) begin |
if (cp_edge & cp) begin |
shifter <= {shifter[8:0], serial_in}; |
end |
end |
assign parallel_out = enable ? shifter : 10'h000; |
|
always @(posedge sysclk) begin |
if (cp_edge & ~cp) begin |
serial_out <= shifter[9]; |
end |
end |
|
|
`include "../common/functions.vh" |
|
endmodule |
/rtl/verilog/i4003/i4003_tb.v
0,0 → 1,104
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 i40031 Shift Register testbench |
// |
// This testbench instantiates two i4003 modules, resets them, and |
// shifts a single '1' bit through them. |
// |
// 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 i4003_tb; |
|
// Inputs |
reg sysclk = 1'b0; |
reg cp; |
reg serial_in; |
reg enable; |
|
// Interconnection |
wire serial_chain; |
|
// Outputs |
wire [19:0] parallel_out; |
wire serial_out; |
|
// Instantiate the Unit Under Test (UUT) |
i4003 #( |
.SYSCLK_TCY (50) |
) uut1 ( |
.sysclk (sysclk), |
.cp (cp), |
.serial_in (serial_in), |
.enable (enable), |
.parallel_out (parallel_out[9:0]), |
.serial_out (serial_chain) |
); |
|
// Instantiate the Unit Under Test (UUT) |
i4003 #( |
.SYSCLK_TCY (50) |
) uut2 ( |
.sysclk (sysclk), |
.cp (cp), |
.serial_in (serial_chain), |
.enable (enable), |
.parallel_out (parallel_out[19:10]), |
.serial_out (serial_out) |
); |
|
always begin |
#25 sysclk = ~sysclk; |
end |
|
integer i; |
|
initial begin |
// Initialize Inputs |
cp = 1'b0; |
serial_in = 1'b0; |
enable = 1'b0; |
|
// Wait 100 ns for global reset to finish |
#100; |
enable = 1'b1; |
|
// Shift a 1 down the registers |
shift(1'b1); |
for (i = 0; i < 21; i = i + 1) |
shift(1'b0); |
|
$finish; |
end |
|
task shift( |
input si |
); |
begin |
cp = 1'b1; |
#250; |
serial_in = si; |
#2750; |
serial_in = 1'b0; |
#3000; |
cp = 1'b0; |
#6000; |
end |
endtask |
|
endmodule |
|
/rtl/verilog/i4004/alu.v
1,18 → 1,18
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// |
// 4004 Arithmetic Logic Unit |
// |
// |
// This file is part of the MCS-4 project hosted at OpenCores: |
// http://www.opencores.org/cores/mcs-4/ |
// |
|
// |
// |
// Copyright © 2012, 2021 by Reece Pollack <rrpollack@opencores.org> |
// |
// These materials are provided under the Creative Commons |
// "Attribution-NonCommercial-ShareAlike" Public License. They |
// are NOT "public domain" and are protected by copyright. |
// |
// "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. |
20,222 → 20,222
//////////////////////////////////////////////////////////////////////// |
|
module alu( |
input wire sysclk, |
|
// Inputs from the Timing and I/O board |
input wire a12, |
input wire m12, |
input wire x12, |
input wire poc, |
|
// Common 4-bit data bus |
inout wire [3:0] data, |
input wire sysclk, |
|
// Outputs to the Instruction Decode board |
output wire acc_0, |
output wire add_0, |
output reg cy_1, |
// Inputs from the Timing and I/O board |
input wire a12, |
input wire m12, |
input wire x12, |
input wire poc, |
|
// Inputs from the Instruction Decode board |
input wire cma, |
input wire write_acc_1, |
input wire write_carry_2, |
input wire read_acc_3, |
input wire add_group_4, |
input wire inc_group_5, |
input wire sub_group_6, |
input wire ior, |
input wire iow, |
input wire ral, |
input wire rar, |
input wire ope_n, |
input wire daa, |
input wire dcl, |
input wire inc_isz, |
input wire kbp, |
input wire o_ib, |
input wire tcs, |
input wire xch, |
input wire n0342, |
input wire x21_clk2, |
input wire x31_clk2, |
input wire com_n, |
|
// Outputs to external pins |
output wire cmram0, |
output wire cmram1, |
output wire cmram2, |
output wire cmram3, |
output wire cmrom |
); |
// Common 4-bit data bus |
inout wire [3:0] data, |
|
reg [3:0] acc; |
reg cy; |
// Outputs to the Instruction Decode board |
output wire acc_0, |
output wire add_0, |
output reg cy_1, |
|
// Decode logic |
wire n0854 = ~(~x12); |
wire n0351 = ~(x21_clk2 | ~dcl); |
wire n0415 = ~(x21_clk2 | ope_n); |
wire add_ib = ~(x31_clk2 | ~inc_isz); |
wire cy_ib = ~(x31_clk2 | ~iow); |
wire acb_ib = ~((x31_clk2 | ~xch) & (x21_clk2 | ~iow)); |
wire n0477 = ~((~x31_clk2 & ~ior) | (a12 & ior)); |
wire adc_cy = ~(write_carry_2 | n0477); |
wire add_acc = ~(write_acc_1 | n0477); |
wire adsr = ~(x31_clk2 | ~rar); |
wire adsl = ~(x31_clk2 | ~ral); |
wire acc_adac = ~(~cma | n0342); |
wire acc_ada = ~(read_acc_3 | n0342); |
wire cy_ada = ~(add_group_4 | n0342); |
wire cy_adac = ~(sub_group_6 | n0342); |
// Inputs from the Instruction Decode board |
input wire cma, |
input wire write_acc_1, |
input wire write_carry_2, |
input wire read_acc_3, |
input wire add_group_4, |
input wire inc_group_5, |
input wire sub_group_6, |
input wire ior, |
input wire iow, |
input wire ral, |
input wire rar, |
input wire ope_n, |
input wire daa, |
input wire dcl, |
input wire inc_isz, |
input wire kbp, |
input wire o_ib, |
input wire tcs, |
input wire xch, |
input wire n0342, |
input wire x21_clk2, |
input wire x31_clk2, |
input wire com_n, |
|
// Latch the incoming data bus |
reg [3:0] tmp; // It's the name used in simulator! |
always @(posedge sysclk) begin |
if (~n0342) |
tmp <= data; |
if (m12) |
tmp <= 4'b1111; |
end |
|
// Invert some of the incoming data |
reg n0893, n0891, n0889, n0887; // D3, D2, D1, D0 |
always @(posedge sysclk) begin |
if (sub_group_6) begin |
n0887 <= tmp[0]; |
n0889 <= ~tmp[1]; |
n0891 <= tmp[2]; |
n0893 <= ~tmp[3]; |
end |
if (~(sub_group_6 | m12)) begin |
n0887 <= ~tmp[0]; |
n0889 <= tmp[1]; |
n0891 <= ~tmp[2]; |
n0893 <= tmp[3]; |
end |
end |
// Outputs to external pins |
output wire cmram0, |
output wire cmram1, |
output wire cmram2, |
output wire cmram3, |
output wire cmrom |
); |
|
// Feedback from Accumulator |
reg n0873, n0872, n0871, n0870; |
always @(posedge sysclk) begin |
if (acc_ada) |
{n0873, n0872, n0871, n0870} <= acc ^ 4'b1010; |
if (acc_adac) |
{n0873, n0872, n0871, n0870} <= acc ^ 4'b0101; |
if (m12) |
{n0873, n0872, n0871, n0870} <= 4'b1010; |
end |
reg [3:0] acc; |
reg cy; |
|
// Carry generator |
wire n0546 = ~(inc_group_5 | n0342); |
reg n0550; |
always @(posedge sysclk) begin |
if (m12) n0550 <= 1'b0; |
if (n0546) n0550 <= 1'b1; |
if (cy_adac) n0550 <= ~cy; |
if (cy_ada) n0550 <= cy; |
end |
wire n0911 = ~(n0550 ? (n0887 | n0870) : (n0887 & n0870)); |
wire n0553 = n0911; |
wire n0912 = ~(n0553 ? (n0889 | n0871) : (n0889 & n0871)); |
wire n0556 = n0912; |
wire n0913 = ~(n0556 ? (n0891 | n0872) : (n0891 & n0872)); |
wire n0559 = n0913; |
wire n0914 = ~(n0559 ? (n0893 | n0873) : (n0893 & n0873)); |
wire n0861 = n0914; |
// Decode logic |
wire n0854 = ~(~x12); |
wire n0351 = ~(x21_clk2 | ~dcl); |
wire n0415 = ~(x21_clk2 | ope_n); |
wire add_ib = ~(x31_clk2 | ~inc_isz); |
wire cy_ib = ~(x31_clk2 | ~iow); |
wire acb_ib = ~((x31_clk2 | ~xch) & (x21_clk2 | ~iow)); |
wire n0477 = ~((~x31_clk2 & ~ior) | (a12 & ior)); |
wire adc_cy = ~(write_carry_2 | n0477); |
wire add_acc = ~(write_acc_1 | n0477); |
wire adsr = ~(x31_clk2 | ~rar); |
wire adsl = ~(x31_clk2 | ~ral); |
wire acc_adac = ~(~cma | n0342); |
wire acc_ada = ~(read_acc_3 | n0342); |
wire cy_ada = ~(add_group_4 | n0342); |
wire cy_adac = ~(sub_group_6 | n0342); |
|
// Adder |
wire n0878 = ~((n0887 & n0550 & n0870) | (n0553 & (n0887 | n0870 | n0550))); |
wire n0875 = ~((n0889 & n0553 & n0871) | (n0556 & (n0889 | n0871 | n0553))); |
wire n0879 = ~((n0891 & n0556 & n0872) | (n0559 & (n0891 | n0872 | n0556))); |
wire n0877 = ~((n0893 & n0559 & n0873) | (n0861 & (n0893 | n0873 | n0559))); |
wire n0846 = ~n0878; |
wire n0847 = n0875; |
wire n0848 = ~n0879; |
wire n0514 = n0877; |
// Latch the incoming data bus |
reg [3:0] tmp; // It's the name used in simulator! |
always @(posedge sysclk) begin |
if (~n0342) |
tmp <= data; |
if (m12) |
tmp <= 4'b1111; |
end |
|
// Shifter / Accumulator and Carry |
reg [3:0] acc_out; // {n0356, n0348, n0347, n0346} |
wire n0803 = ~((acc_out[3] & (acc_out[2] | acc_out[1])) | cy_1); |
wire n0403 = ~(~daa | n0803); |
wire [3:0] acc_in = {n0514, n0848, n0847, n0846}; |
always @(posedge sysclk) begin |
if (adsr) |
{acc, cy} <= {cy_1, acc_in}; |
if (add_acc) |
acc <= acc_in; |
if (adsl) |
{cy, acc} <= {acc_in, cy_1}; |
if (adc_cy) |
cy <= n0861; |
if (n0403 & n0415) |
cy <= 1'b1; |
// Dynamic refresh would occur during M12 |
end |
|
// Accumulator output latch |
always @(posedge sysclk) begin |
if (n0854) begin |
cy_1 <= cy; |
acc_out <= acc; |
end |
end |
assign acc_0 = ~|acc_out; |
assign add_0 = ~|acc_in; |
// Invert some of the incoming data |
reg n0893, n0891, n0889, n0887; // D3, D2, D1, D0 |
always @(posedge sysclk) begin |
if (sub_group_6) begin |
n0887 <= tmp[0]; |
n0889 <= ~tmp[1]; |
n0891 <= tmp[2]; |
n0893 <= ~tmp[3]; |
end |
if (~(sub_group_6 | m12)) begin |
n0887 <= ~tmp[0]; |
n0889 <= tmp[1]; |
n0891 <= ~tmp[2]; |
n0893 <= tmp[3]; |
end |
end |
|
// Keyboard Process logic |
wire n0378 = ~((daa & n0803) | o_ib); |
wire n0345 = kbp & (acc_out == 4'b1000); |
wire n0354 = kbp & (acc_out == 4'b0100); |
wire n0363 = kbp & (acc_out == 4'b0010); |
wire n0370 = kbp & (acc_out == 4'b0001); |
wire n0377 = (kbp & (acc_out == 4'b0000)) | ~n0378; |
wire n0358 = ~(n0345 | n0354 | n0363 | n0370 | n0377 | n0403); |
wire n0366 = ~( n0354 | n0363 | n0370 | n0377 | tcs ); |
wire n0359 = ~(n0345 | n0370 | n0377 | tcs ); |
wire n0357 = ~(n0345 | n0363 | n0377 | n0403); |
// Feedback from Accumulator |
reg n0873, n0872, n0871, n0870; |
always @(posedge sysclk) begin |
if (acc_ada) |
{n0873, n0872, n0871, n0870} <= acc ^ 4'b1010; |
if (acc_adac) |
{n0873, n0872, n0871, n0870} <= acc ^ 4'b0101; |
if (m12) |
{n0873, n0872, n0871, n0870} <= 4'b1010; |
end |
|
// Data output mux |
reg [3:0] dout; |
always @(*) begin |
dout = 4'bzzzz; |
if (acb_ib) |
dout = acc_out; |
if (add_ib) |
dout = acc_in; |
if (cy_ib) |
dout = {3'bxxx, cy_1}; |
if (n0415) |
dout = {n0358, n0366, n0359, n0357}; |
end |
assign data = dout; |
|
// Generate CMROM / CMRAMn |
// This may get moved to the Timing & I/O board |
// Inputs: {n0355, n0364, n0371}, n0351, poc, com_n |
wire n0355 = ~acc_out[2]; |
wire n0364 = ~acc_out[1]; |
wire n0371 = ~acc_out[0]; |
reg n0749, n0750, n0751; |
always @(posedge sysclk) begin |
if (poc) begin |
n0749 <= 1'b1; |
n0750 <= 1'b1; |
n0751 <= 1'b1; |
end |
else begin |
if (n0351) begin |
n0749 <= n0355; |
n0750 <= n0364; |
n0751 <= n0371; |
end |
end |
end |
// Carry generator |
wire n0546 = ~(inc_group_5 | n0342); |
reg n0550; |
always @(posedge sysclk) begin |
if (m12) n0550 <= 1'b0; |
if (n0546) n0550 <= 1'b1; |
if (cy_adac) n0550 <= ~cy; |
if (cy_ada) n0550 <= cy; |
end |
wire n0911 = ~(n0550 ? (n0887 | n0870) : (n0887 & n0870)); |
wire n0553 = n0911; |
wire n0912 = ~(n0553 ? (n0889 | n0871) : (n0889 & n0871)); |
wire n0556 = n0912; |
wire n0913 = ~(n0556 ? (n0891 | n0872) : (n0891 & n0872)); |
wire n0559 = n0913; |
wire n0914 = ~(n0559 ? (n0893 | n0873) : (n0893 & n0873)); |
wire n0861 = n0914; |
|
assign cmram3 = ~(com_n | n0749); |
assign cmram2 = ~(com_n | n0750); |
assign cmram1 = ~(com_n | n0751); |
assign cmram0 = ~(com_n | ~n0749 | ~n0750 | ~n0751); |
assign cmrom = ~(com_n | poc); |
|
// Adder |
wire n0878 = ~((n0887 & n0550 & n0870) | (n0553 & (n0887 | n0870 | n0550))); |
wire n0875 = ~((n0889 & n0553 & n0871) | (n0556 & (n0889 | n0871 | n0553))); |
wire n0879 = ~((n0891 & n0556 & n0872) | (n0559 & (n0891 | n0872 | n0556))); |
wire n0877 = ~((n0893 & n0559 & n0873) | (n0861 & (n0893 | n0873 | n0559))); |
wire n0846 = ~n0878; |
wire n0847 = n0875; |
wire n0848 = ~n0879; |
wire n0514 = n0877; |
|
// Shifter / Accumulator and Carry |
reg [3:0] acc_out; // {n0356, n0348, n0347, n0346} |
wire n0803 = ~((acc_out[3] & (acc_out[2] | acc_out[1])) | cy_1); |
wire n0403 = ~(~daa | n0803); |
wire [3:0] acc_in = {n0514, n0848, n0847, n0846}; |
always @(posedge sysclk) begin |
if (adsr) |
{acc, cy} <= {cy_1, acc_in}; |
if (add_acc) |
acc <= acc_in; |
if (adsl) |
{cy, acc} <= {acc_in, cy_1}; |
if (adc_cy) |
cy <= n0861; |
if (n0403 & n0415) |
cy <= 1'b1; |
// Dynamic refresh would occur during M12 |
end |
|
// Accumulator output latch |
always @(posedge sysclk) begin |
if (n0854) begin |
cy_1 <= cy; |
acc_out <= acc; |
end |
end |
assign acc_0 = ~|acc_out; |
assign add_0 = ~|acc_in; |
|
// Keyboard Process logic |
wire n0378 = ~((daa & n0803) | o_ib); |
wire n0345 = kbp & (acc_out == 4'b1000); |
wire n0354 = kbp & (acc_out == 4'b0100); |
wire n0363 = kbp & (acc_out == 4'b0010); |
wire n0370 = kbp & (acc_out == 4'b0001); |
wire n0377 = (kbp & (acc_out == 4'b0000)) | ~n0378; |
wire n0358 = ~(n0345 | n0354 | n0363 | n0370 | n0377 | n0403); |
wire n0366 = ~( n0354 | n0363 | n0370 | n0377 | tcs ); |
wire n0359 = ~(n0345 | n0370 | n0377 | tcs ); |
wire n0357 = ~(n0345 | n0363 | n0377 | n0403); |
|
// Data output mux |
reg [3:0] dout; |
always @(*) begin |
dout = 4'bzzzz; |
if (acb_ib) |
dout = acc_out; |
if (add_ib) |
dout = acc_in; |
if (cy_ib) |
dout = {3'bxxx, cy_1}; |
if (n0415) |
dout = {n0358, n0366, n0359, n0357}; |
end |
assign data = dout; |
|
// Generate CMROM / CMRAMn |
// This may get moved to the Timing & I/O board |
// Inputs: {n0355, n0364, n0371}, n0351, poc, com_n |
wire n0355 = ~acc_out[2]; |
wire n0364 = ~acc_out[1]; |
wire n0371 = ~acc_out[0]; |
reg n0749, n0750, n0751; |
always @(posedge sysclk) begin |
if (poc) begin |
n0749 <= 1'b1; |
n0750 <= 1'b1; |
n0751 <= 1'b1; |
end |
else begin |
if (n0351) begin |
n0749 <= n0355; |
n0750 <= n0364; |
n0751 <= n0371; |
end |
end |
end |
|
assign cmram3 = ~(com_n | n0749); |
assign cmram2 = ~(com_n | n0750); |
assign cmram1 = ~(com_n | n0751); |
assign cmram0 = ~(com_n | ~n0749 | ~n0750 | ~n0751); |
assign cmrom = ~(com_n | poc); |
|
endmodule |
/rtl/verilog/i4004/i4004.v
1,18 → 1,18
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// |
// 4004 CPU Integration Module |
// |
// |
// This file is part of the MCS-4 project hosted at OpenCores: |
// http://www.opencores.org/cores/mcs-4/ |
// |
|
// |
// |
// Copyright © 2012, 2021 by Reece Pollack <rrpollack@opencores.org> |
// |
// These materials are provided under the Creative Commons |
// "Attribution-NonCommercial-ShareAlike" Public License. They |
// are NOT "public domain" and are protected by copyright. |
// |
// "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. |
20,270 → 20,270
//////////////////////////////////////////////////////////////////////// |
|
module i4004( |
input wire sysclk, |
input wire clk1_pad, |
input wire clk2_pad, |
input wire poc_pad, |
input wire test_pad, |
inout wire [3:0] data_pad, |
output wire cmrom_pad, |
output wire cmram0_pad, |
output wire cmram1_pad, |
output wire cmram2_pad, |
output wire cmram3_pad, |
output wire sync_pad |
input wire sysclk, |
input wire clk1_pad, |
input wire clk2_pad, |
input wire poc_pad, |
input wire test_pad, |
inout wire [3:0] data_pad, |
output wire cmrom_pad, |
output wire cmram0_pad, |
output wire cmram1_pad, |
output wire cmram2_pad, |
output wire cmram3_pad, |
output wire sync_pad |
); |
|
// Common BiDir data bus |
wire [3:0] data; |
// Common BiDir data bus |
wire [3:0] data; |
|
// Timing and I/O Board Outputs |
wire clk1; |
wire clk2; |
wire a12; |
wire a22; |
wire a32; |
wire m12; |
wire m22; |
wire x12; |
wire x22; |
wire x32; |
wire gate; |
wire poc; // Clean POC_PAD |
wire n0432; // Clean TEST_PAD |
// Timing and I/O Board Outputs |
wire clk1; |
wire clk2; |
wire a12; |
wire a22; |
wire a32; |
wire m12; |
wire m22; |
wire x12; |
wire x22; |
wire x32; |
wire gate; |
wire poc; // Clean POC_PAD |
wire n0432; // Clean TEST_PAD |
|
// Outputs from the Instruction Decode board |
wire jcn_isz; |
wire jin_fin; |
wire jun_jms; |
wire cn_n; |
wire bbl; |
wire jms; |
wire sc; |
wire dc; |
wire n0636; |
wire sc_m22_clk2; |
wire fin_fim_src_jin; |
wire inc_isz_add_sub_xch_ld; |
wire inc_isz_xch; |
wire opa0_n; |
wire cma; |
wire write_acc_1; |
wire write_carry_2; |
wire read_acc_3; |
wire add_group_4; |
wire inc_group_5; |
wire sub_group_6; |
wire ior; |
wire iow; |
wire ral; |
wire rar; |
wire ope_n; |
wire daa; |
wire dcl; |
wire inc_isz; |
wire kbp; |
wire o_ib; |
wire tcs; |
wire xch; |
wire n0342; |
wire x21_clk2; |
wire x31_clk2; |
wire com_n; |
// Outputs from the Instruction Decode board |
wire jcn_isz; |
wire jin_fin; |
wire jun_jms; |
wire cn_n; |
wire bbl; |
wire jms; |
wire sc; |
wire dc; |
wire n0636; |
wire sc_m22_clk2; |
wire fin_fim_src_jin; |
wire inc_isz_add_sub_xch_ld; |
wire inc_isz_xch; |
wire opa0_n; |
wire cma; |
wire write_acc_1; |
wire write_carry_2; |
wire read_acc_3; |
wire add_group_4; |
wire inc_group_5; |
wire sub_group_6; |
wire ior; |
wire iow; |
wire ral; |
wire rar; |
wire ope_n; |
wire daa; |
wire dcl; |
wire inc_isz; |
wire kbp; |
wire o_ib; |
wire tcs; |
wire xch; |
wire n0342; |
wire x21_clk2; |
wire x31_clk2; |
wire com_n; |
|
// Outputs from the ALU board |
wire acc_0; |
wire add_0; |
wire cy_1; |
wire cmram0; |
wire cmram1; |
wire cmram2; |
wire cmram3; |
wire cmrom; |
// Outputs from the ALU board |
wire acc_0; |
wire add_0; |
wire cy_1; |
wire cmram0; |
wire cmram1; |
wire cmram2; |
wire cmram3; |
wire cmrom; |
|
// Instantiate the Timing and I/O board |
timing_io tio_board ( |
.sysclk(sysclk), |
.clk1_pad(clk1_pad), |
.clk2_pad(clk2_pad), |
.poc_pad(poc_pad), |
.ior(ior), |
.clk1(clk1), |
.clk2(clk2), |
.a12(a12), |
.a22(a22), |
.a32(a32), |
.m12(m12), |
.m22(m22), |
.x12(x12), |
.x22(x22), |
.x32(x32), |
.gate(gate), |
.poc(poc), |
.data(data), |
.data_pad(data_pad), |
.test_pad(test_pad), |
.n0432(n0432), |
.sync_pad(sync_pad), |
.cmrom(cmrom), |
.cmrom_pad(cmrom_pad), |
.cmram0(cmram0), |
.cmram0_pad(cmram0_pad), |
.cmram1(cmram1), |
.cmram1_pad(cmram1_pad), |
.cmram2(cmram2), |
.cmram2_pad(cmram2_pad), |
.cmram3(cmram3), |
.cmram3_pad(cmram3_pad) |
); |
// Instantiate the Timing and I/O board |
timing_io tio_board ( |
.sysclk(sysclk), |
.clk1_pad(clk1_pad), |
.clk2_pad(clk2_pad), |
.poc_pad(poc_pad), |
.ior(ior), |
.clk1(clk1), |
.clk2(clk2), |
.a12(a12), |
.a22(a22), |
.a32(a32), |
.m12(m12), |
.m22(m22), |
.x12(x12), |
.x22(x22), |
.x32(x32), |
.gate(gate), |
.poc(poc), |
.data(data), |
.data_pad(data_pad), |
.test_pad(test_pad), |
.n0432(n0432), |
.sync_pad(sync_pad), |
.cmrom(cmrom), |
.cmrom_pad(cmrom_pad), |
.cmram0(cmram0), |
.cmram0_pad(cmram0_pad), |
.cmram1(cmram1), |
.cmram1_pad(cmram1_pad), |
.cmram2(cmram2), |
.cmram2_pad(cmram2_pad), |
.cmram3(cmram3), |
.cmram3_pad(cmram3_pad) |
); |
|
// Instantiate the Instruction Decode board |
instruction_decode id_board ( |
.sysclk(sysclk), |
.clk1(clk1), |
.clk2(clk2), |
.a22(a22), |
.m12(m12), |
.m22(m22), |
.x12(x12), |
.x22(x22), |
.x32(x32), |
.poc(poc), |
.n0432(n0432), |
.data(data), |
.jcn_isz(jcn_isz), |
.jin_fin(jin_fin), |
.jun_jms(jun_jms), |
.cn_n(cn_n), |
.bbl(bbl), |
.jms(jms), |
.sc(sc), |
.dc(dc), |
.n0636(n0636), |
.sc_m22_clk2(sc_m22_clk2), |
.fin_fim_src_jin(fin_fim_src_jin), |
.inc_isz_add_sub_xch_ld(inc_isz_add_sub_xch_ld), |
.inc_isz_xch(inc_isz_xch), |
.opa0_n(opa0_n), |
.acc_0(acc_0), |
.add_0(add_0), |
.cy_1(cy_1), |
.cma(cma), |
.write_acc_1(write_acc_1), |
.write_carry_2(write_carry_2), |
.read_acc_3(read_acc_3), |
.add_group_4(add_group_4), |
.inc_group_5(inc_group_5), |
.sub_group_6(sub_group_6), |
.ior(ior), |
.iow(iow), |
.ral(ral), |
.rar(rar), |
.ope_n(ope_n), |
.daa(daa), |
.dcl(dcl), |
.inc_isz(inc_isz), |
.kbp(kbp), |
.o_ib(o_ib), |
.tcs(tcs), |
.xch(xch), |
.n0342(n0342), |
.x21_clk2(x21_clk2), |
.x31_clk2(x31_clk2), |
.com_n(com_n) |
); |
// Instantiate the Instruction Decode board |
instruction_decode id_board ( |
.sysclk(sysclk), |
.clk1(clk1), |
.clk2(clk2), |
.a22(a22), |
.m12(m12), |
.m22(m22), |
.x12(x12), |
.x22(x22), |
.x32(x32), |
.poc(poc), |
.n0432(n0432), |
.data(data), |
.jcn_isz(jcn_isz), |
.jin_fin(jin_fin), |
.jun_jms(jun_jms), |
.cn_n(cn_n), |
.bbl(bbl), |
.jms(jms), |
.sc(sc), |
.dc(dc), |
.n0636(n0636), |
.sc_m22_clk2(sc_m22_clk2), |
.fin_fim_src_jin(fin_fim_src_jin), |
.inc_isz_add_sub_xch_ld(inc_isz_add_sub_xch_ld), |
.inc_isz_xch(inc_isz_xch), |
.opa0_n(opa0_n), |
.acc_0(acc_0), |
.add_0(add_0), |
.cy_1(cy_1), |
.cma(cma), |
.write_acc_1(write_acc_1), |
.write_carry_2(write_carry_2), |
.read_acc_3(read_acc_3), |
.add_group_4(add_group_4), |
.inc_group_5(inc_group_5), |
.sub_group_6(sub_group_6), |
.ior(ior), |
.iow(iow), |
.ral(ral), |
.rar(rar), |
.ope_n(ope_n), |
.daa(daa), |
.dcl(dcl), |
.inc_isz(inc_isz), |
.kbp(kbp), |
.o_ib(o_ib), |
.tcs(tcs), |
.xch(xch), |
.n0342(n0342), |
.x21_clk2(x21_clk2), |
.x31_clk2(x31_clk2), |
.com_n(com_n) |
); |
|
// Instantiate the ALU board |
alu alu_board ( |
.sysclk(sysclk), |
.a12(a12), |
.m12(m12), |
.x12(x12), |
.poc(poc), |
.data(data), |
.acc_0(acc_0), |
.add_0(add_0), |
.cy_1(cy_1), |
.cma(cma), |
.write_acc_1(write_acc_1), |
.write_carry_2(write_carry_2), |
.read_acc_3(read_acc_3), |
.add_group_4(add_group_4), |
.inc_group_5(inc_group_5), |
.sub_group_6(sub_group_6), |
.ior(ior), |
.iow(iow), |
.ral(ral), |
.rar(rar), |
.ope_n(ope_n), |
.daa(daa), |
.dcl(dcl), |
.inc_isz(inc_isz), |
.kbp(kbp), |
.o_ib(o_ib), |
.tcs(tcs), |
.xch(xch), |
.n0342(n0342), |
.x21_clk2(x21_clk2), |
.x31_clk2(x31_clk2), |
.com_n(com_n), |
.cmram0(cmram0), |
.cmram1(cmram1), |
.cmram2(cmram2), |
.cmram3(cmram3), |
.cmrom(cmrom) |
); |
// Instantiate the ALU board |
alu alu_board ( |
.sysclk(sysclk), |
.a12(a12), |
.m12(m12), |
.x12(x12), |
.poc(poc), |
.data(data), |
.acc_0(acc_0), |
.add_0(add_0), |
.cy_1(cy_1), |
.cma(cma), |
.write_acc_1(write_acc_1), |
.write_carry_2(write_carry_2), |
.read_acc_3(read_acc_3), |
.add_group_4(add_group_4), |
.inc_group_5(inc_group_5), |
.sub_group_6(sub_group_6), |
.ior(ior), |
.iow(iow), |
.ral(ral), |
.rar(rar), |
.ope_n(ope_n), |
.daa(daa), |
.dcl(dcl), |
.inc_isz(inc_isz), |
.kbp(kbp), |
.o_ib(o_ib), |
.tcs(tcs), |
.xch(xch), |
.n0342(n0342), |
.x21_clk2(x21_clk2), |
.x31_clk2(x31_clk2), |
.com_n(com_n), |
.cmram0(cmram0), |
.cmram1(cmram1), |
.cmram2(cmram2), |
.cmram3(cmram3), |
.cmrom(cmrom) |
); |
|
// Instantiate the Instruction Pointer board |
instruction_pointer ip_board ( |
.sysclk(sysclk), |
.clk1(clk1), |
.clk2(clk2), |
.a12(a12), |
.a22(a22), |
.a32(a32), |
.m12(m12), |
.m22(m22), |
.x12(x12), |
.x22(x22), |
.x32(x32), |
.poc(poc), |
.m12_m22_clk1_m11_m12(gate), |
.data(data), |
.jcn_isz(jcn_isz), |
.jin_fin(jin_fin), |
.jun_jms(jun_jms), |
.cn_n(cn_n), |
.bbl(bbl), |
.jms(jms), |
.sc(sc), |
.dc(dc) |
); |
// Instantiate the Instruction Pointer board |
instruction_pointer ip_board ( |
.sysclk(sysclk), |
.clk1(clk1), |
.clk2(clk2), |
.a12(a12), |
.a22(a22), |
.a32(a32), |
.m12(m12), |
.m22(m22), |
.x12(x12), |
.x22(x22), |
.x32(x32), |
.poc(poc), |
.m12_m22_clk1_m11_m12(gate), |
.data(data), |
.jcn_isz(jcn_isz), |
.jin_fin(jin_fin), |
.jun_jms(jun_jms), |
.cn_n(cn_n), |
.bbl(bbl), |
.jms(jms), |
.sc(sc), |
.dc(dc) |
); |
|
// Instantiate the Scratchpad board |
scratchpad sp_board ( |
.sysclk(sysclk), |
.clk1(clk1), |
.clk2(clk2), |
.a12(a12), |
.a22(a22), |
.a32(a32), |
.m12(m12), |
.m22(m22), |
.x12(x12), |
.x22(x22), |
.x32(x32), |
.poc(poc), |
.m12_m22_clk1_m11_m12(gate), |
.data(data), |
.n0636(n0636), |
.sc_m22_clk2(sc_m22_clk2), |
.fin_fim_src_jin(fin_fim_src_jin), |
.inc_isz_add_sub_xch_ld(inc_isz_add_sub_xch_ld), |
.inc_isz_xch(inc_isz_xch), |
.opa0_n(opa0_n), |
.sc(sc), |
.dc(dc) |
); |
// Instantiate the Scratchpad board |
scratchpad sp_board ( |
.sysclk(sysclk), |
.clk1(clk1), |
.clk2(clk2), |
.a12(a12), |
.a22(a22), |
.a32(a32), |
.m12(m12), |
.m22(m22), |
.x12(x12), |
.x22(x22), |
.x32(x32), |
.poc(poc), |
.m12_m22_clk1_m11_m12(gate), |
.data(data), |
.n0636(n0636), |
.sc_m22_clk2(sc_m22_clk2), |
.fin_fim_src_jin(fin_fim_src_jin), |
.inc_isz_add_sub_xch_ld(inc_isz_add_sub_xch_ld), |
.inc_isz_xch(inc_isz_xch), |
.opa0_n(opa0_n), |
.sc(sc), |
.dc(dc) |
); |
|
endmodule |
/rtl/verilog/i4004/i4004_tb.v
0,0 → 1,174
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 i4004 CPU testbench |
// |
// This module is a testbench for the i4004 and related modules. |
// |
// This testbench instantiates an i4004 CPU module, i4001 and |
// i4001_rom ROM modules, and an i4002 RAM module, so that the |
// simulation can be compared with that of Lagos Kintle's software |
// emulator. |
// |
// 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 i4004_tb; |
|
localparam SYSCLK_TCY = 50; // 20 MHz Oscillator period in ns |
// localparam SYSCLK_TCY = 20; // 50 MHz Oscillator period in ns |
|
// Inputs |
reg sysclk = 1'b0; |
reg poc_pad = 1'b1; |
reg test_pad = 1'b1; |
reg clear_pad = 1'b0; |
wire clk1_pad; |
wire clk2_pad; |
|
// Outputs |
wire cmrom_pad; |
wire cmram0_pad; |
wire cmram1_pad; |
wire cmram2_pad; |
wire cmram3_pad; |
wire sync_pad; |
|
// Bidirs |
wire [3:0] data_pad; |
wire [3:0] io_pad; |
|
// Generate a system clock |
always begin |
sysclk = 1'b0; |
#(SYSCLK_TCY / 2); |
sysclk = 1'b1; |
#(SYSCLK_TCY / 2); |
end |
|
// Instantiate the 2-phase clock generator |
clockgen #( |
.SYSCLK_TCY (SYSCLK_TCY) |
) clockgen ( |
.sysclk (sysclk), |
.clk1 (clk1_pad), |
.clk2 (clk2_pad) |
); |
|
// Instantiate a 4001 ROM chip |
wire [11:0] rom_addr; |
wire [ 7:0] rom_data; |
i4001 #( |
.ROM_NUMBER(4'd0), |
.IO_OUTPUT(4'b1111) |
) rom_0 ( |
.sysclk(sysclk), |
.clk1_pad(clk1_pad), |
.clk2_pad(clk2_pad), |
.sync_pad(sync_pad), |
.poc_pad(poc_pad), |
.cmrom_pad(cmrom_pad), |
.data_pad(data_pad), |
.io_pad(io_pad), |
.clear_pad(clear_pad), |
.rom_addr(rom_addr), |
.rom_data(rom_data) |
); |
|
i4001_rom #( |
.ROM_NUMBER(4'd0) |
) rom_store ( |
.sysclk(sysclk), |
.rom_addr(rom_addr), |
.rom_data(rom_data) |
); |
|
// Instantiate a 4002 RAM chip |
wire [3:0] oport; |
i4002 ram_0 ( |
.sysclk (sysclk), |
.clk1 (clk1_pad), |
.clk2 (clk2_pad), |
.sync (sync_pad), |
.reset (poc_pad), |
.cm (cmram0_pad), |
.data (data_pad), |
.oport (oport), |
.ram0_addr2 (5'h00), |
.ram0_data2_out (), |
.ram1_addr2 (5'h00), |
.ram1_data2_out (), |
.ram2_addr2 (5'h00), |
.ram2_data2_out (), |
.ram3_addr2 (5'h00), |
.ram3_data2_out () |
); |
|
// Instantiate the Unit Under Test (UUT) |
i4004 i4004 ( |
.clk1_pad(clk1_pad), |
.clk2_pad(clk2_pad), |
.poc_pad(poc_pad), |
.test_pad(test_pad), |
.data_pad(data_pad), |
.cmrom_pad(cmrom_pad), |
.cmram0_pad(cmram0_pad), |
.cmram1_pad(cmram1_pad), |
.cmram2_pad(cmram2_pad), |
.cmram3_pad(cmram3_pad), |
.sync_pad(sync_pad) |
); |
|
// Simplify access to internal registers |
wire [3:0] acc = i4004.alu_board.acc; |
wire cy = i4004.alu_board.cy; |
|
wire [7:0] r0r1 = i4004.sp_board.dram_array[0]; |
wire [7:0] r2r3 = i4004.sp_board.dram_array[1]; |
wire [7:0] r4r5 = i4004.sp_board.dram_array[2]; |
wire [7:0] r6r7 = i4004.sp_board.dram_array[3]; |
wire [7:0] r8r9 = i4004.sp_board.dram_array[4]; |
wire [7:0] rarb = i4004.sp_board.dram_array[5]; |
wire [7:0] rcrd = i4004.sp_board.dram_array[6]; |
wire [7:0] rerf = i4004.sp_board.dram_array[7]; |
|
wire [11:0] ip0 = i4004.ip_board.dram_array[0]; |
wire [11:0] ip1 = i4004.ip_board.dram_array[1]; |
wire [11:0] ip2 = i4004.ip_board.dram_array[2]; |
wire [11:0] ip3 = i4004.ip_board.dram_array[3]; |
wire [1:0] ptr = i4004.ip_board.addr_ptr; |
|
// Count cycles to make it easier to sync with the analyzer |
integer cycle = 1; |
always @(posedge clk1_pad or negedge clk1_pad or |
posedge clk2_pad or negedge clk2_pad) begin |
cycle = cycle + 1; |
if (cycle == 1100) begin |
// Bring us out of reset |
poc_pad = 1'b0; |
end |
end |
|
// initial begin |
// // Wait for 33 SYNCs to allow the i4002 RAMs to initialize |
// repeat (33) |
// @(posedge sync_pad); |
|
// // Bring us out of reset |
// poc_pad = 1'b0; |
// end |
|
endmodule |
/rtl/verilog/i4004/instruction_decode.v
1,18 → 1,18
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// |
// 4004 Instruction Decoder |
// |
// |
// This file is part of the MCS-4 project hosted at OpenCores: |
// http://www.opencores.org/cores/mcs-4/ |
// |
|
// |
// |
// Copyright © 2012, 2021 by Reece Pollack <rrpollack@opencores.org> |
// |
// These materials are provided under the Creative Commons |
// "Attribution-NonCommercial-ShareAlike" Public License. They |
// are NOT "public domain" and are protected by copyright. |
// |
// "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. |
20,239 → 20,239
//////////////////////////////////////////////////////////////////////// |
|
module instruction_decode( |
input wire sysclk, // 50 MHz FPGA clock |
|
// Inputs from the Timing and I/O board |
input wire clk1, |
input wire clk2, |
input wire a22, |
input wire m12, |
input wire m22, |
input wire x12, |
input wire x22, |
input wire x32, |
input wire poc, // Power-On Clear (reset) |
input wire n0432, // Conditioned TEST_PAD |
input wire sysclk, // 50 MHz FPGA clock |
|
// Common 4-bit data bus |
inout wire [3:0] data, |
// Inputs from the Timing and I/O board |
input wire clk1, |
input wire clk2, |
input wire a22, |
input wire m12, |
input wire m22, |
input wire x12, |
input wire x22, |
input wire x32, |
input wire poc, // Power-On Clear (reset) |
input wire n0432, // Conditioned TEST_PAD |
|
// These drive the Instruction Pointer (IP) board |
output wire jcn_isz, // JCN+ISZ |
output wire jin_fin, // JIN+FIN |
output wire jun_jms, // JUN+JMS |
output wire cn_n, // ~CN |
output wire bbl, // BBL |
output wire jms, // JMS |
|
// Outputs to both the IP and SP boards |
output wire sc, // SC (Single Cycle) |
output wire dc, // DC (Double Cycle, ~SC) |
// Common 4-bit data bus |
inout wire [3:0] data, |
|
// Outputs to the Scratch Pad (SP) board |
output wire n0636, // JIN+FIN |
output wire sc_m22_clk2, // SC&M22&CLK2 |
output wire fin_fim_src_jin, // FIN+FIM+SRC+JIN |
output wire inc_isz_add_sub_xch_ld, // INC+ISZ+ADD+SUB+XCH+LD |
output wire inc_isz_xch, // INC+ISZ+XCH |
output wire opa0_n, // ~OPA.0 |
// These drive the Instruction Pointer (IP) board |
output wire jcn_isz, // JCN+ISZ |
output wire jin_fin, // JIN+FIN |
output wire jun_jms, // JUN+JMS |
output wire cn_n, // ~CN |
output wire bbl, // BBL |
output wire jms, // JMS |
|
// Inputs from the ALU board (condition bits) |
input wire acc_0, // ACC_0 |
input wire add_0, // ADD_0 |
input wire cy_1, // CY_1 |
// Outputs to both the IP and SP boards |
output wire sc, // SC (Single Cycle) |
output wire dc, // DC (Double Cycle, ~SC) |
|
// Outputs to the Arithmetic Logic Unit (ALU) board |
output wire cma, |
output wire write_acc_1, |
output wire write_carry_2, |
output wire read_acc_3, |
output wire add_group_4, |
output wire inc_group_5, |
output wire sub_group_6, |
output wire ior, |
output wire iow, |
output wire ral, |
output wire rar, |
output wire ope_n, |
output wire daa, |
output wire dcl, |
output wire inc_isz, |
output wire kbp, |
output wire o_ib, |
output wire tcs, |
output wire xch, |
output wire n0342, |
output wire x21_clk2, |
output wire x31_clk2, |
output wire com_n |
); |
|
wire sc_m12_clk2 = sc & m12 & clk2; |
assign sc_m22_clk2 = sc & m22 & clk2; |
// Outputs to the Scratch Pad (SP) board |
output wire n0636, // JIN+FIN |
output wire sc_m22_clk2, // SC&M22&CLK2 |
output wire fin_fim_src_jin, // FIN+FIM+SRC+JIN |
output wire inc_isz_add_sub_xch_ld, // INC+ISZ+ADD+SUB+XCH+LD |
output wire inc_isz_xch, // INC+ISZ+XCH |
output wire opa0_n, // ~OPA.0 |
|
// Latch the first 4 bits of the opcode |
reg [3:0] opr = 4'b0000; |
always @(posedge sysclk) begin |
if (sc_m12_clk2) |
opr <= data; |
end |
|
// Latch the second 4 bits of the opcode |
reg [3:0] opa = 4'b0000; |
always @(posedge sysclk) begin |
if (sc_m22_clk2) |
opa <= data; |
end |
assign opa0_n = ~opa[0]; |
|
// Full OPR Decoding |
wire nop = (opr == 4'b0000); |
wire jcn = (opr == 4'b0001); |
wire fim_src = (opr == 4'b0010); |
assign jin_fin = (opr == 4'b0011); |
wire jun = (opr == 4'b0100); |
assign jms = (opr == 4'b0101); |
wire inc = (opr == 4'b0110); |
wire isz = (opr == 4'b0111); |
wire add = (opr == 4'b1000); |
wire sub = (opr == 4'b1001); |
wire ld = (opr == 4'b1010); |
assign xch = (opr == 4'b1011); |
assign bbl = (opr == 4'b1100); |
wire ldm = (opr == 4'b1101); |
wire io = (opr == 4'b1110); |
wire ope = (opr == 4'b1111); |
|
assign ope_n = ~ope; |
assign n0636 = jin_fin; |
assign jcn_isz = jcn | isz; |
assign jun_jms = jun | jms; |
wire ldm_bbl = ldm | bbl; |
assign inc_isz = (inc | isz) & sc; |
assign inc_isz_xch = inc | isz | xch; |
assign inc_isz_add_sub_xch_ld = inc | isz | add | sub | xch | ld; |
assign fin_fim_src_jin = fim_src | jin_fin; |
|
// OPE: OPA Decoding |
assign o_ib = ope & (opa[3] == 1'b0); |
wire clb = ope & (opa == 4'b0000); |
wire clc = ope & (opa == 4'b0001); |
wire iac = ope & (opa == 4'b0010); |
wire cmc = ope & (opa == 4'b0011); |
assign cma = ope & (opa == 4'b0100); |
assign ral = ope & (opa == 4'b0101); |
assign rar = ope & (opa == 4'b0110); |
wire tcc = ope & (opa == 4'b0111); |
|
wire dac = ope & (opa == 4'b1000); |
assign tcs = ope & (opa == 4'b1001); |
wire stc = ope & (opa == 4'b1010); |
assign daa = ope & (opa == 4'b1011); |
assign kbp = ope & (opa == 4'b1100); |
assign dcl = ope & (opa == 4'b1101); |
// Inputs from the ALU board (condition bits) |
input wire acc_0, // ACC_0 |
input wire add_0, // ADD_0 |
input wire cy_1, // CY_1 |
|
// IO: OPA Decoding |
assign iow = io & (opa[3] == 1'b0); |
assign ior = io & (opa[3] == 1'b1); |
wire adm = io & (opa == 4'b1011); |
wire sbm = io & (opa == 4'b1000); |
// Outputs to the Arithmetic Logic Unit (ALU) board |
output wire cma, |
output wire write_acc_1, |
output wire write_carry_2, |
output wire read_acc_3, |
output wire add_group_4, |
output wire inc_group_5, |
output wire sub_group_6, |
output wire ior, |
output wire iow, |
output wire ral, |
output wire rar, |
output wire ope_n, |
output wire daa, |
output wire dcl, |
output wire inc_isz, |
output wire kbp, |
output wire o_ib, |
output wire tcs, |
output wire xch, |
output wire n0342, |
output wire x21_clk2, |
output wire x31_clk2, |
output wire com_n |
); |
|
wire fin_fim = fin_fim_src_jin & ~opa[0]; |
wire src = fim_src & opa[0]; |
|
assign write_acc_1 = ~(kbp | tcs | daa | xch | poc | cma | tcc | dac | iac | |
clb | ior | ld | sub | add | ldm_bbl); |
assign write_carry_2 = ~(tcs | poc | tcc | stc | cmc | dac | iac | |
clc | clb | sbm | adm | sub | add); |
assign read_acc_3 = ~(daa | rar | ral | dac | iac | sbm | adm | sub | add); |
assign add_group_4 = ~(tcs | tcc | adm | add); |
assign inc_group_5 = ~(inc_isz | stc | iac); |
assign sub_group_6 = ~(cmc | sbm | sub | m12); |
wire sc_m12_clk2 = sc & m12 & clk2; |
assign sc_m22_clk2 = sc & m22 & clk2; |
|
// The Condition Flip-Flop |
reg n0397; |
wire n0486 = ~((opa[2] & acc_0) | (opa[1] & cy_1) | (opa[0] & n0432)); |
wire n0419 = ~((add_0 | ~isz) & (~jcn | ((n0486 | opa[3]) & (~n0486 | ~opa[3])))); |
wire n0413 = ~((sc & n0419 & x32) | (~x32 & n0397)); |
reg n0405; |
always @(posedge sysclk) begin |
if (clk2) |
n0405 <= n0413; |
end |
always @(posedge sysclk) begin |
if (clk1) |
n0397 <= ~n0405; |
end |
assign cn_n = ~n0397; |
// Latch the first 4 bits of the opcode |
reg [3:0] opr = 4'b0000; |
always @(posedge sysclk) begin |
if (sc_m12_clk2) |
opr <= data; |
end |
|
// The Single-Cycle Flip-Flop |
reg n0343 = 1'b0; |
wire n0368 = ~((sc & (fin_fim | jcn_isz | jun_jms) & x32) | (n0343 & ~x32)); |
reg n0362 = 1'b1; |
always @(posedge sysclk) begin |
if (clk2) |
n0362 <= n0368; |
if (clk1) |
n0343 <= ~n0362; |
end |
assign sc = ~n0343; |
assign dc = ~sc; |
// Latch the second 4 bits of the opcode |
reg [3:0] opa = 4'b0000; |
always @(posedge sysclk) begin |
if (sc_m22_clk2) |
opa <= data; |
end |
assign opa0_n = ~opa[0]; |
|
// Generate ~(X21&~CLK2) |
reg n0360; |
always @(posedge sysclk) begin |
if (clk2) |
n0360 <= ~x12; |
end |
wire n0337 = ~(n0360 | clk2); |
assign x21_clk2 = ~n0337; |
// Full OPR Decoding |
wire nop = (opr == 4'b0000); |
wire jcn = (opr == 4'b0001); |
wire fim_src = (opr == 4'b0010); |
assign jin_fin = (opr == 4'b0011); |
wire jun = (opr == 4'b0100); |
assign jms = (opr == 4'b0101); |
wire inc = (opr == 4'b0110); |
wire isz = (opr == 4'b0111); |
wire add = (opr == 4'b1000); |
wire sub = (opr == 4'b1001); |
wire ld = (opr == 4'b1010); |
assign xch = (opr == 4'b1011); |
assign bbl = (opr == 4'b1100); |
wire ldm = (opr == 4'b1101); |
wire io = (opr == 4'b1110); |
wire ope = (opr == 4'b1111); |
|
// Generate ~(X31&~CLK2) |
reg n0380; |
always @(posedge sysclk) begin |
if (clk2) |
n0380 <= ~x22; |
end |
wire n0375 = ~(n0380 | clk2); |
assign x31_clk2 = ~n0375; |
|
// Generate ~COM |
wire n0329 = io; |
assign ope_n = ~ope; |
assign n0636 = jin_fin; |
assign jcn_isz = jcn | isz; |
assign jun_jms = jun | jms; |
wire ldm_bbl = ldm | bbl; |
assign inc_isz = (inc | isz) & sc; |
assign inc_isz_xch = inc | isz | xch; |
assign inc_isz_add_sub_xch_ld = inc | isz | add | sub | xch | ld; |
assign fin_fim_src_jin = fim_src | jin_fin; |
|
reg n0414, n0797; |
always @(posedge sysclk) begin |
if (clk2) |
n0414 <= a22; |
else |
n0797 <= n0414; |
end |
// OPE: OPA Decoding |
assign o_ib = ope & (opa[3] == 1'b0); |
wire clb = ope & (opa == 4'b0000); |
wire clc = ope & (opa == 4'b0001); |
wire iac = ope & (opa == 4'b0010); |
wire cmc = ope & (opa == 4'b0011); |
assign cma = ope & (opa == 4'b0100); |
assign ral = ope & (opa == 4'b0101); |
assign rar = ope & (opa == 4'b0110); |
wire tcc = ope & (opa == 4'b0111); |
|
reg n0433, n0801; |
always @(posedge sysclk) begin |
if (clk2) |
n0433 <= m12; |
else |
n0801 <= n0433; |
end |
|
reg n0425, n0805; |
always @(posedge sysclk) begin |
if (clk2) |
n0425 <= x12; |
else |
n0805 <= n0425; |
end |
wire dac = ope & (opa == 4'b1000); |
assign tcs = ope & (opa == 4'b1001); |
wire stc = ope & (opa == 4'b1010); |
assign daa = ope & (opa == 4'b1011); |
assign kbp = ope & (opa == 4'b1100); |
assign dcl = ope & (opa == 4'b1101); |
|
wire n0782 = ~((n0801 & n0329) | (src & n0805) | n0797); |
assign com_n = n0782; |
|
// Generate N0342 |
wire n0332 = ~(((n0329 | poc) & x22 & clk2) | (~(n0329 | poc) & n0337 & clk1)); |
assign n0342 = n0332; |
// IO: OPA Decoding |
assign iow = io & (opa[3] == 1'b0); |
assign ior = io & (opa[3] == 1'b1); |
wire adm = io & (opa == 4'b1011); |
wire sbm = io & (opa == 4'b1000); |
|
// Output OPA onto the data bus |
wire opa_ib = (ldm_bbl | jun_jms) & ~x21_clk2; |
assign data = opa_ib ? opa : 4'bzzzz; |
wire fin_fim = fin_fim_src_jin & ~opa[0]; |
wire src = fim_src & opa[0]; |
|
assign write_acc_1 = ~(kbp | tcs | daa | xch | poc | cma | tcc | dac | iac | |
clb | ior | ld | sub | add | ldm_bbl); |
assign write_carry_2 = ~(tcs | poc | tcc | stc | cmc | dac | iac | |
clc | clb | sbm | adm | sub | add); |
assign read_acc_3 = ~(daa | rar | ral | dac | iac | sbm | adm | sub | add); |
assign add_group_4 = ~(tcs | tcc | adm | add); |
assign inc_group_5 = ~(inc_isz | stc | iac); |
assign sub_group_6 = ~(cmc | sbm | sub | m12); |
|
// The Condition Flip-Flop |
reg n0397; |
wire n0486 = ~((opa[2] & acc_0) | (opa[1] & cy_1) | (opa[0] & n0432)); |
wire n0419 = ~((add_0 | ~isz) & (~jcn | ((n0486 | opa[3]) & (~n0486 | ~opa[3])))); |
wire n0413 = ~((sc & n0419 & x32) | (~x32 & n0397)); |
reg n0405; |
always @(posedge sysclk) begin |
if (clk2) |
n0405 <= n0413; |
end |
always @(*) begin |
if (clk1) |
n0397 <= ~n0405; |
end |
assign cn_n = ~n0397; |
|
// The Single-Cycle Flip-Flop |
reg n0343 = 1'b0; |
wire n0368 = ~((sc & (fin_fim | jcn_isz | jun_jms) & x32) | (n0343 & ~x32)); |
reg n0362 = 1'b1; |
always @(posedge sysclk) begin |
if (clk2) |
n0362 <= n0368; |
if (clk1) |
n0343 <= ~n0362; |
end |
assign sc = ~n0343; |
assign dc = ~sc; |
|
// Generate ~(X21&~CLK2) |
reg n0360; |
always @(posedge sysclk) begin |
if (clk2) |
n0360 <= ~x12; |
end |
wire n0337 = ~(n0360 | clk2); |
assign x21_clk2 = ~n0337; |
|
// Generate ~(X31&~CLK2) |
reg n0380; |
always @(posedge sysclk) begin |
if (clk2) |
n0380 <= ~x22; |
end |
wire n0375 = ~(n0380 | clk2); |
assign x31_clk2 = ~n0375; |
|
// Generate ~COM |
wire n0329 = io; |
|
reg n0414, n0797; |
always @(posedge sysclk) begin |
if (clk2) |
n0414 <= a22; |
else |
n0797 <= n0414; |
end |
|
reg n0433, n0801; |
always @(posedge sysclk) begin |
if (clk2) |
n0433 <= m12; |
else |
n0801 <= n0433; |
end |
|
reg n0425, n0805; |
always @(posedge sysclk) begin |
if (clk2) |
n0425 <= x12; |
else |
n0805 <= n0425; |
end |
|
wire n0782 = ~((n0801 & n0329) | (src & n0805) | n0797); |
assign com_n = n0782; |
|
// Generate N0342 |
wire n0332 = ~(((n0329 | poc) & x22 & clk2) | (~(n0329 | poc) & n0337 & clk1)); |
assign n0342 = n0332; |
|
// Output OPA onto the data bus |
wire opa_ib = (ldm_bbl | jun_jms) & ~x21_clk2; |
assign data = opa_ib ? opa : 4'bzzzz; |
|
endmodule |
/rtl/verilog/i4004/instruction_pointer.v
1,18 → 1,18
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// |
// 4004 Instruction Pointer Array |
// |
// |
// This file is part of the MCS-4 project hosted at OpenCores: |
// http://www.opencores.org/cores/mcs-4/ |
// |
|
// |
// |
// Copyright © 2012, 2021 by Reece Pollack <rrpollack@opencores.org> |
// |
// These materials are provided under the Creative Commons |
// "Attribution-NonCommercial-ShareAlike" Public License. They |
// are NOT "public domain" and are protected by copyright. |
// |
// "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. |
20,195 → 20,212
//////////////////////////////////////////////////////////////////////// |
|
module instruction_pointer ( |
input wire sysclk, // 50 MHz FPGA clock |
input wire sysclk, // 50 MHz FPGA clock |
|
// Inputs from the Timing and I/O board |
input wire clk1, |
input wire clk2, |
input wire a12, |
input wire a22, |
input wire a32, |
input wire m12, |
input wire m22, |
input wire x12, |
input wire x22, |
input wire x32, |
input wire poc, // Power-On Clear (reset) |
input wire m12_m22_clk1_m11_m12, // M12+M22+CLK1~(M11+M12) |
// Inputs from the Timing and I/O board |
input wire clk1, |
input wire clk2, |
input wire a12, |
input wire a22, |
input wire a32, |
input wire m12, |
input wire m22, |
input wire x12, |
input wire x22, |
input wire x32, |
input wire poc, // Power-On Clear (reset) |
input wire m12_m22_clk1_m11_m12, // M12+M22+CLK1~(M11+M12) |
|
// Common 4-bit data bus |
inout wire [3:0] data, |
// Common 4-bit data bus |
inout wire [3:0] data, |
|
// Inputs from the Instruction Decode board |
input wire jcn_isz, // JCN+ISZ |
input wire jin_fin, // JIN+FIN |
input wire jun_jms, // JUN+JMS |
input wire cn_n, // ~CN |
input wire bbl, // BBL |
input wire jms, // JMS |
input wire sc, // SC (Single Cycle) |
input wire dc // DC (Double Cycle, ~SC) |
); |
// Inputs from the Instruction Decode board |
input wire jcn_isz, // JCN+ISZ |
input wire jin_fin, // JIN+FIN |
input wire jun_jms, // JUN+JMS |
input wire cn_n, // ~CN |
input wire bbl, // BBL |
input wire jms, // JMS |
input wire sc, // SC (Single Cycle) |
input wire dc // DC (Double Cycle, ~SC) |
); |
|
reg [11:0] dram_array [0:3]; |
reg [11:0] dram_temp; |
wire [3:0] din_n; |
reg [11:0] dram_array [0:3]; |
reg [11:0] dram_temp; |
wire [3:0] din_n; |
|
wire inh = (jin_fin & sc) | ((jun_jms | (jcn_isz & ~cn_n)) & dc); |
wire inh = (jin_fin & sc) | ((jun_jms | (jcn_isz & ~cn_n)) & dc); |
|
// Row Counter stuff |
wire [1:0] addr_ptr; // Effective Address counter |
wire addr_ptr_step; // CLK2(JMS&DC&M22+BBL(M22+X12+X22)) |
|
assign addr_ptr_step = ~(~clk2 | ~(((m22 | x12 | x22) & bbl) | |
(m22 & dc & jms))); |
counter addr_ptr_0 ( |
.sysclk(sysclk), |
.step_a(clk1), |
.step_b(addr_ptr_step), |
.q(addr_ptr[0]) |
); |
counter addr_ptr_1 ( |
.sysclk(sysclk), |
.step_a( addr_ptr[0]), |
.step_b(~addr_ptr[0]), |
.q(addr_ptr[1]) |
); |
// Row Counter stuff |
wire [1:0] addr_ptr; // Effective Address counter |
wire addr_ptr_step; // CLK2(JMS&DC&M22+BBL(M22+X12+X22)) |
wire n0459; |
wire n0466; |
|
// Refresh counter stuff |
wire [1:0] addr_rfsh; // Row Refresh counter |
wire addr_rfsh_step; // (~INH)&X32&CLK2 |
|
assign addr_rfsh_step = ~inh & x32 & clk2; |
|
counter addr_rfsh_0 ( |
.sysclk(sysclk), |
.step_a(clk1), |
.step_b(addr_rfsh_step), |
.q(addr_rfsh[0]) |
); |
counter addr_rfsh_1 ( |
.sysclk(sysclk), |
.step_a( addr_rfsh[0]), |
.step_b(~addr_rfsh[0]), |
.q(addr_rfsh[1]) |
); |
assign addr_ptr_step = ~(~clk2 | ~(((m22 | x12 | x22) & bbl) | |
(m22 & dc & jms))); |
counter addr_ptr_0 ( |
.sysclk(sysclk), |
.step_a_in(clk1), |
.step_b_in(addr_ptr_step), |
.step_a_out(n0459), |
.step_b_out(n0466), |
.q(), |
.qn(addr_ptr[0]) |
); |
counter addr_ptr_1 ( |
.sysclk(sysclk), |
.step_a_in(n0459), |
.step_b_in(n0466), |
.step_a_out(), |
.step_b_out(), |
.q(), |
.qn(addr_ptr[1]) |
); |
|
// Row selection mux |
reg [1:0] row; // {N0409, N0420} |
always @(posedge sysclk) begin |
if (x12) |
row <= addr_rfsh; |
if (x32) |
row <= addr_ptr; |
end |
// Refresh counter stuff |
wire [1:0] addr_rfsh; // Row Refresh counter |
wire addr_rfsh_step; // (~INH)&X32&CLK2 |
wire n0455; |
wire n0463; |
|
assign addr_rfsh_step = ~inh & x32 & clk2; |
|
// Row Precharge/Read/Write stuff |
wire precharge; // (~INH)(X11+X31)CLK1 |
wire row_read; // (~POC)CLK2(X12+X32)~INH |
wire row_write; // ((~SC)(JIN+FIN))CLK1(M11+X21~INH) |
counter addr_rfsh_0 ( |
.sysclk(sysclk), |
.step_a_in(clk1), |
.step_b_in(addr_rfsh_step), |
.step_a_out(n0455), |
.step_b_out(n0463), |
.q(), |
.qn(addr_rfsh[0]) |
); |
counter addr_rfsh_1 ( |
.sysclk(sysclk), |
.step_a_in(n0455), |
.step_b_in(n0463), |
.step_a_out(), |
.step_b_out(), |
.q(), |
.qn(addr_rfsh[1]) |
); |
|
reg n0517; |
always @(posedge sysclk) begin |
if (clk2) |
n0517 <= ~(m22 | x22); |
end |
assign precharge = ~(n0517 | inh | ~clk1); |
// Row selection mux |
reg [1:0] row; // {N0409, N0420} |
always @(posedge sysclk) begin |
if (x12) |
row <= addr_rfsh; |
if (x32) |
row <= addr_ptr; |
end |
|
assign row_read = ~poc & clk2 & (x12 | x32) & ~inh; |
|
reg n0438; |
always @(posedge sysclk) begin |
if (clk2) |
n0438 <= ~((x12 & ~inh) | a32); |
end |
assign row_write = ~(n0438 | (jin_fin & ~sc) | ~clk1); |
|
// Row Precharge/Read/Write stuff |
wire precharge; // (~INH)(X11+X31)CLK1 |
wire row_read; // (~POC)CLK2(X12+X32)~INH |
wire row_write; // ((~SC)(JIN+FIN))CLK1(M11+X21~INH) |
|
// Column Read selection stuff |
reg n0416; |
always @(posedge sysclk) begin |
if (clk2) |
n0416 <= ~x32; |
end |
wire radb0 = ~(n0416 | clk2); |
reg n0517; |
always @(posedge sysclk) begin |
if (clk2) |
n0517 <= ~(m22 | x22); |
end |
assign precharge = ~(n0517 | inh | ~clk1); |
|
reg n0384; |
always @(posedge sysclk) begin |
if (clk2) |
n0384 <= ~a12; |
end |
wire radb1 = ~(n0384 | clk2); |
assign row_read = ~poc & clk2 & (x12 | x32) & ~inh; |
|
reg n0374; |
always @(posedge sysclk) begin |
if (clk2) |
n0374 <= ~a22; |
end |
wire radb2 = ~(n0374 | clk2); |
reg n0438; |
always @(posedge sysclk) begin |
if (clk2) |
n0438 <= ~((x12 & ~inh) | a32); |
end |
assign row_write = ~(n0438 | (jin_fin & ~sc) | ~clk1); |
|
|
// Column Write selection stuff |
wire n0322 = ~(sc | cn_n); |
wire wadb0 = ~(~clk2 | ~(a12 | (sc & jin_fin & x32) | |
(((jcn_isz & n0322) | (jun_jms & ~sc)) & m22))); |
wire wadb1 = ~(~clk2 | ~(a22 | (sc & jin_fin & x22) | |
(((jcn_isz & n0322) | (jun_jms & ~sc)) & m12))); |
wire wadb2 = ~(~clk2 | ~(a32 | (jun_jms & ~sc & x22))); |
// Column Read selection stuff |
reg n0416; |
always @(posedge sysclk) begin |
if (clk2) |
n0416 <= ~x32; |
end |
wire radb0 = ~(n0416 | clk2); |
|
reg n0384; |
always @(posedge sysclk) begin |
if (clk2) |
n0384 <= ~a12; |
end |
wire radb1 = ~(n0384 | clk2); |
|
// Manage the row data buffer |
always @(posedge sysclk) begin |
if (precharge) |
dram_temp <= 12'b0; |
|
if (row_read) |
dram_temp <= dram_array[row]; |
|
if (wadb0) |
dram_temp[ 3:0] <= ~din_n; |
if (wadb1) |
dram_temp[ 7:4] <= ~din_n; |
if (wadb2) |
dram_temp[11:8] <= ~din_n; |
end |
reg n0374; |
always @(posedge sysclk) begin |
if (clk2) |
n0374 <= ~a22; |
end |
wire radb2 = ~(n0374 | clk2); |
|
// Handle row writes |
always @(posedge sysclk) begin |
if (row_write) |
dram_array[row] <= dram_temp; |
end |
|
// Manage the data output mux |
reg [3:0] dout; |
always @* begin |
(* PARALLEL_CASE *) |
case (1'b1) |
radb0: dout = dram_temp[ 3:0]; |
radb1: dout = dram_temp[ 7:4]; |
radb2: dout = dram_temp[11:8]; |
default: dout = 4'bzzzz; |
endcase |
end |
assign data = dout; |
|
// Data In mux and incrementer |
reg [3:0] incr_in; |
always @(posedge sysclk) begin |
if (m12_m22_clk1_m11_m12) |
incr_in <= data; |
end |
// Column Write selection stuff |
wire n0322 = ~(sc | cn_n); |
wire wadb0 = ~(~clk2 | ~(a12 | (sc & jin_fin & x32) | |
(((jcn_isz & n0322) | (jun_jms & ~sc)) & m22))); |
wire wadb1 = ~(~clk2 | ~(a22 | (sc & jin_fin & x22) | |
(((jcn_isz & n0322) | (jun_jms & ~sc)) & m12))); |
wire wadb2 = ~(~clk2 | ~(a32 | (jun_jms & ~sc & x22))); |
|
reg carry_out, carry_in; |
wire [4:0] incr_out = (a12 | ((a22 | a32) & carry_in)) ? |
(incr_in + 4'b0001) : {1'b0, incr_in}; |
always @(posedge sysclk) begin |
if (clk2) |
carry_out <= incr_out[4]; |
if (clk1) |
carry_in <= carry_out; |
end |
assign din_n = ~incr_out[3:0]; |
|
// Manage the row data buffer |
wire [11:0] row_data = dram_array[row]; |
always @(posedge sysclk) begin |
if (precharge) |
dram_temp <= 12'b0; |
|
if (row_read) |
dram_temp <= row_data; |
|
if (wadb0) |
dram_temp[ 3:0] <= ~din_n; |
if (wadb1) |
dram_temp[ 7:4] <= ~din_n; |
if (wadb2) |
dram_temp[11:8] <= ~din_n; |
end |
|
// Handle row writes |
always @(posedge sysclk) begin |
if (row_write) |
dram_array[row] <= dram_temp; |
end |
|
// Manage the data output mux |
reg [3:0] dout; |
always @(*) begin |
(* PARALLEL_CASE *) |
case (1'b1) |
radb0: dout = dram_temp[ 3:0]; |
radb1: dout = dram_temp[ 7:4]; |
radb2: dout = dram_temp[11:8]; |
default: dout = 4'bzzzz; |
endcase |
end |
assign data = dout; |
|
// Data In mux and incrementer |
reg [3:0] incr_in; |
always @(posedge sysclk) begin |
if (m12_m22_clk1_m11_m12) |
incr_in <= data; |
end |
|
reg carry_out, carry_in; |
wire [4:0] incr_out = (a12 | ((a22 | a32) & carry_in)) ? |
(incr_in + 4'b0001) : {1'b0, incr_in}; |
always @(posedge sysclk) begin |
if (clk2) |
carry_out <= incr_out[4]; |
if (clk1) |
carry_in <= carry_out; |
end |
assign din_n = ~incr_out[3:0]; |
|
endmodule |
/rtl/verilog/i4004/scratchpad.v
1,18 → 1,18
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// |
// 4004 Scratchpad Register Array |
// |
// |
// This file is part of the MCS-4 project hosted at OpenCores: |
// http://www.opencores.org/cores/mcs-4/ |
// |
|
// |
// |
// Copyright © 2012, 2021 by Reece Pollack <rrpollack@opencores.org> |
// |
// These materials are provided under the Creative Commons |
// "Attribution-NonCommercial-ShareAlike" Public License. They |
// are NOT "public domain" and are protected by copyright. |
// |
// "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. |
20,149 → 20,163
//////////////////////////////////////////////////////////////////////// |
|
module scratchpad ( |
input wire sysclk, // 50 MHz FPGA clock |
input wire sysclk, // 50 MHz FPGA clock |
|
// Inputs from the Timing and I/O board |
input wire clk1, |
input wire clk2, |
input wire a12, |
input wire a22, |
input wire a32, |
input wire m12, |
input wire m22, |
input wire x12, |
input wire x22, |
input wire x32, |
input wire poc, // Power-On Clear (reset) |
input wire m12_m22_clk1_m11_m12, // M12+M22+CLK1~(M11+M12) |
// Inputs from the Timing and I/O board |
input wire clk1, |
input wire clk2, |
input wire a12, |
input wire a22, |
input wire a32, |
input wire m12, |
input wire m22, |
input wire x12, |
input wire x22, |
input wire x32, |
input wire poc, // Power-On Clear (reset) |
input wire m12_m22_clk1_m11_m12, // M12+M22+CLK1~(M11+M12) |
|
// Common 4-bit data bus |
inout wire [3:0] data, |
// Common 4-bit data bus |
inout wire [3:0] data, |
|
// Inputs from the Instruction Decode board |
input wire n0636, // JIN+FIN |
input wire sc_m22_clk2, // SC&M22&CLK2 |
input wire fin_fim_src_jin, // FIN+FIM+SRC+JIN |
input wire inc_isz_add_sub_xch_ld, // INC+ISZ+ADD+SUB+XCH+LD |
input wire inc_isz_xch, // INC+ISZ+XCH |
input wire opa0_n, // ~OPA.0 |
input wire sc, // SC (Single Cycle) |
input wire dc // DC (Double Cycle, ~SC) |
); |
// Inputs from the Instruction Decode board |
input wire n0636, // JIN+FIN |
input wire sc_m22_clk2, // SC&M22&CLK2 |
input wire fin_fim_src_jin, // FIN+FIM+SRC+JIN |
input wire inc_isz_add_sub_xch_ld, // INC+ISZ+ADD+SUB+XCH+LD |
input wire inc_isz_xch, // INC+ISZ+XCH |
input wire opa0_n, // ~OPA.0 |
input wire sc, // SC (Single Cycle) |
input wire dc // DC (Double Cycle, ~SC) |
); |
|
reg [7:0] dram_array [0:7]; |
reg [7:0] dram_temp; |
reg [3:0] din_n; |
reg [7:0] dram_array [0:7]; |
reg [7:0] dram_temp; |
reg [3:0] din_n; |
|
// Refresh counter stuff |
wire [2:0] reg_rfsh; // Row Refresh counter |
wire reg_rfsh_step; // SC&A12&CLK2 |
|
assign reg_rfsh_step = sc & a12 & clk2; |
|
counter reg_rfsh_0 ( |
.sysclk(sysclk), |
.step_a(clk1), |
.step_b(reg_rfsh_step), |
.q(reg_rfsh[0]) |
); |
counter reg_rfsh_1 ( |
.sysclk(sysclk), |
.step_a( reg_rfsh[0]), |
.step_b(~reg_rfsh[0]), |
.q(reg_rfsh[1]) |
); |
counter reg_rfsh_2 ( |
.sysclk(sysclk), |
.step_a( reg_rfsh[1]), |
.step_b(~reg_rfsh[1]), |
.q(reg_rfsh[2]) |
); |
// Refresh counter stuff |
wire [2:0] reg_rfsh; // Row Refresh counter |
wire reg_rfsh_step; // SC&A12&CLK2 |
wire n0571; |
wire n0574; |
wire n0600; |
wire n0610; |
|
// Row selection mux |
reg [2:0] row; // {N0646, N0617, N0582} |
always @(posedge sysclk) begin |
if (sc & a22) |
row <= reg_rfsh; |
if (sc_m22_clk2) |
row <= data[3:1]; |
end |
assign reg_rfsh_step = sc & a12 & clk2; |
|
counter reg_rfsh_0 ( |
.sysclk(sysclk), |
.step_a_in(clk1), |
.step_b_in(reg_rfsh_step), |
.step_a_out(n0571), |
.step_b_out(n0574), |
.q(reg_rfsh[0]), |
.qn() |
); |
counter reg_rfsh_1 ( |
.sysclk(sysclk), |
.step_a_in(n0571), |
.step_b_in(n0574), |
.step_a_out(n0600), |
.step_b_out(n0610), |
.q(reg_rfsh[1]), |
.qn() |
); |
counter reg_rfsh_2 ( |
.sysclk(sysclk), |
.step_a_in(n0600), |
.step_b_in(n0610), |
.step_a_out(), |
.step_b_out(), |
.q(reg_rfsh[2]), |
.qn() |
); |
|
// Row Precharge/Read/Write stuff |
wire precharge; // SC(A22+M22)CLK2 |
wire row_read; // (~POC)&CLK2&SC(A32+X12) |
wire row_write; // CLK2&SC(A12+M12) |
// Row selection mux |
reg [2:0] row; // {N0646, N0617, N0582} |
always @(posedge sysclk) begin |
if (sc & a22) |
row <= reg_rfsh; |
if (sc_m22_clk2) |
row <= data[3:1]; |
end |
|
assign precharge = sc & (a22 | m22) & clk2; |
assign row_read = ~(poc | ~(clk2 & sc & (a32 | x12))); |
assign row_write = sc & (a12 | m12) & clk2; |
|
// Row Precharge/Read/Write stuff |
wire precharge; // SC(A22+M22)CLK2 |
wire row_read; // (~POC)&CLK2&SC(A32+X12) |
wire row_write; // CLK2&SC(A12+M12) |
|
// Column Read selection stuff |
reg n0615; |
always @(posedge sysclk) begin |
if (clk2) |
n0615 <= ~(x12 & (fin_fim_src_jin | |
(opa0_n & inc_isz_add_sub_xch_ld))); |
end |
wire rrab0 = ~(dc | n0615 | clk2); |
assign precharge = sc & (a22 | m22) & clk2; |
assign row_read = ~(poc | ~(clk2 & sc & (a32 | x12))); |
assign row_write = sc & (a12 | m12) & clk2; |
|
reg n0592; |
always @(posedge sysclk) begin |
if (clk2) |
n0592 <= ~((x22 & fin_fim_src_jin) | |
(~opa0_n & x12 & inc_isz_add_sub_xch_ld)); |
end |
wire rrab1 = ~(dc | n0592 | clk2); |
|
// Column Read selection stuff |
reg n0615; |
always @(posedge sysclk) begin |
if (clk2) |
n0615 <= ~(x12 & (fin_fim_src_jin | |
(opa0_n & inc_isz_add_sub_xch_ld))); |
end |
wire rrab0 = ~(dc | n0615 | clk2); |
|
// Column Write selection stuff |
wire n0564 = opa0_n & fin_fim_src_jin & dc; |
wire n0568 = inc_isz_xch & x32 & sc; |
wire wrab0 = clk2 & ((m12 & n0564) | ( opa0_n & n0568)); |
wire wrab1 = clk2 & ((m22 & n0564) | (~opa0_n & n0568)); |
reg n0592; |
always @(posedge sysclk) begin |
if (clk2) |
n0592 <= ~((x22 & fin_fim_src_jin) | |
(~opa0_n & x12 & inc_isz_add_sub_xch_ld)); |
end |
wire rrab1 = ~(dc | n0592 | clk2); |
|
|
// Force row 0 if FIN&X12 |
wire fin_x12 = (n0636 & opa0_n) & x12; |
// Column Write selection stuff |
wire n0564 = opa0_n & fin_fim_src_jin & dc; |
wire n0568 = inc_isz_xch & x32 & sc; |
wire wrab0 = clk2 & ((m12 & n0564) | ( opa0_n & n0568)); |
wire wrab1 = clk2 & ((m22 & n0564) | (~opa0_n & n0568)); |
|
// Manage the row data buffer |
always @(posedge sysclk) begin |
if (precharge) |
dram_temp <= 8'b0; |
|
if (row_read) |
dram_temp <= dram_array[fin_x12 ? 3'b000 : row]; |
|
if (wrab0) |
dram_temp[ 7:4] <= ~din_n; |
if (wrab1) |
dram_temp[ 3:0] <= ~din_n; |
end |
|
// Handle row writes |
always @(posedge sysclk) begin |
if (row_write) |
dram_array[row] <= dram_temp; |
end |
// Force row 0 if FIN&X12 |
wire fin_x12 = (n0636 & opa0_n) & x12; |
|
// Manage the data output mux |
reg [3:0] dout; |
always @* begin |
(* PARALLEL_CASE *) |
case (1'b1) |
rrab0: dout = dram_temp[ 7:4]; |
rrab1: dout = dram_temp[ 3:0]; |
default: dout = 4'bzzzz; |
endcase |
end |
assign data = dout; |
|
// Data In latch |
always @(posedge sysclk) begin |
if (m12_m22_clk1_m11_m12) |
din_n <= ~data; |
end |
// Manage the row data buffer |
wire [7:0] row_data = dram_array[fin_x12 ? 3'b000 : row]; |
always @(posedge sysclk) begin |
if (precharge) |
dram_temp <= 8'b0; |
|
if (row_read) |
dram_temp <= row_data; |
|
if (wrab0) |
dram_temp[ 7:4] <= ~din_n; |
if (wrab1) |
dram_temp[ 3:0] <= ~din_n; |
end |
|
// Handle row writes |
always @(posedge sysclk) begin |
if (row_write) |
dram_array[row] <= dram_temp; |
end |
|
// Manage the data output mux |
reg [3:0] dout; |
always @* begin |
(* PARALLEL_CASE *) |
case (1'b1) |
rrab0: dout = dram_temp[ 7:4]; |
rrab1: dout = dram_temp[ 3:0]; |
default: dout = 4'bzzzz; |
endcase |
end |
assign data = dout; |
|
// Data In latch |
always @(posedge sysclk) begin |
if (m12_m22_clk1_m11_m12) |
din_n <= ~data; |
end |
|
endmodule |
/rtl/verilog/i4004/timing_io.v
1,18 → 1,18
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// |
// 4004 Timing and I/O Interfaces |
// |
// |
// This file is part of the MCS-4 project hosted at OpenCores: |
// http://www.opencores.org/cores/mcs-4/ |
// |
|
// |
// |
// Copyright © 2012, 2021 by Reece Pollack <rrpollack@opencores.org> |
// |
// These materials are provided under the Creative Commons |
// "Attribution-NonCommercial-ShareAlike" Public License. They |
// are NOT "public domain" and are protected by copyright. |
// |
// "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. |
20,140 → 20,132
//////////////////////////////////////////////////////////////////////// |
|
module timing_io( |
input wire sysclk, |
input wire clk1_pad, |
input wire clk2_pad, |
input wire poc_pad, |
input wire ior, |
|
// Timing and I/O Board Outputs |
output wire clk1, |
output wire clk2, |
output wire a12, |
output wire a22, |
output wire a32, |
output wire m12, |
output wire m22, |
output wire x12, |
output wire x22, |
output wire x32, |
output wire gate, |
output reg poc, |
|
// External I/O Pad conditioning |
inout wire [3:0] data, |
inout wire [3:0] data_pad, |
input wire test_pad, |
output reg n0432, |
output reg sync_pad, |
input wire cmrom, |
output wire cmrom_pad, |
input wire cmram0, |
output wire cmram0_pad, |
input wire cmram1, |
output wire cmram1_pad, |
input wire cmram2, |
output wire cmram2_pad, |
input wire cmram3, |
output wire cmram3_pad |
input wire sysclk, |
input wire clk1_pad, |
input wire clk2_pad, |
input wire poc_pad, |
input wire ior, |
|
// Timing and I/O Board Outputs |
output wire clk1, |
output wire clk2, |
output wire a12, |
output wire a22, |
output wire a32, |
output wire m12, |
output wire m22, |
output wire x12, |
output wire x22, |
output wire x32, |
output wire gate, |
output reg poc, |
|
// External I/O Pad conditioning |
inout wire [3:0] data, |
inout wire [3:0] data_pad, |
input wire test_pad, |
output reg n0432, |
output wire sync_pad, |
input wire cmrom, |
output wire cmrom_pad, |
input wire cmram0, |
output wire cmram0_pad, |
input wire cmram1, |
output wire cmram1_pad, |
input wire cmram2, |
output wire cmram2_pad, |
input wire cmram3, |
output wire cmram3_pad |
); |
|
// Simple pass-throughs |
assign clk1 = clk1_pad; |
assign clk2 = clk2_pad; |
assign cmrom_pad = cmrom; |
assign cmram0_pad = cmram0; |
assign cmram1_pad = cmram1; |
assign cmram2_pad = cmram2; |
assign cmram3_pad = cmram3; |
// Simple pass-throughs |
assign clk1 = clk1_pad; |
assign clk2 = clk2_pad; |
assign cmrom_pad = cmrom; |
assign cmram0_pad = cmram0; |
assign cmram1_pad = cmram1; |
assign cmram2_pad = cmram2; |
assign cmram3_pad = cmram3; |
|
|
// Generate the 8 execution phase indicators |
reg [0:7] master = 8'h00; |
reg [0:7] slave = 8'h00; |
always @(posedge sysclk) begin |
if (clk2) |
master <= {~|slave[0:6], slave[0:6]}; |
else |
sync_pad <= master[7]; |
// Generate the 8 execution phase indicators |
timing_generator timing_generator ( |
.sysclk (sysclk), |
.clk1 (clk1), |
.clk2 (clk2), |
.a12 (a12), |
.a22 (a22), |
.a32 (a32), |
.m12 (m12), |
.m22 (m22), |
.x12 (x12), |
.x22 (x22), |
.x32 (x32), |
.sync (sync_pad) |
); |
|
if (clk1) |
slave <= master; |
end |
// Generate the DRAM Input Gate signal |
// Properly called M12+M22+CLK1~(M11&M12) |
wire n0279 = ~(a32 | m12); |
reg n0278; |
always @(posedge sysclk) begin |
if (clk2) |
n0278 <= n0279; |
end |
wire n0708 = ~((n0278 & clk1) | m12 | m22); |
assign gate = ~n0708; |
|
assign a12 = slave[0]; |
assign a22 = slave[1]; |
assign a32 = slave[2]; |
assign m12 = slave[3]; |
assign m22 = slave[4]; |
assign x12 = slave[5]; |
assign x22 = slave[6]; |
assign x32 = slave[7]; |
|
// Generate a clean POC signal |
always @(posedge sysclk or posedge poc_pad) begin |
if (poc_pad) poc <= 1'b1; |
else if (a12) poc <= 1'b0; |
end |
|
// Generate the DRAM Input Gate signal |
// Properly called M12+M22+CLK1~(M11&M12) |
wire n0279 = ~(a32 | m12); |
reg n0278; |
always @(posedge sysclk) begin |
if (clk2) |
n0278 <= n0279; |
end |
wire n0708 = ~((n0278 & clk1) | m12 | m22); |
assign gate = ~n0708; |
// Generate a clean ~TEST signal (n0432) |
always @(posedge sysclk) begin |
n0432 <= ~test_pad; |
end |
|
|
// Generate a clean POC signal |
always @(posedge sysclk) begin |
if (poc_pad) poc <= 1'b1; |
else if (a12) poc <= 1'b0; |
else poc <= poc; |
end |
|
// Generate a clean ~TEST signal (n0432) |
always @(posedge sysclk) begin |
n0432 <= ~test_pad; |
end |
// Manage the Data I/O pads |
reg L; |
always @(posedge sysclk) begin |
if (clk2) |
L <= a32 | m12 | (x12 & (ior | poc)); |
end |
|
// Manage the Data I/O pads |
reg L; |
always @(posedge sysclk) begin |
if (clk2) |
L <= a32 | m12 | (x12 & (ior | poc)); |
end |
wire n0702 = ~clk2; |
reg n0685; |
reg n0699; |
reg n0707; |
always @(posedge sysclk) begin |
if (clk1) begin |
n0685 <= ~L; |
n0707 <= L; |
end |
if (n0702) |
n0699 <= ~L; |
end |
wire n0700 = n0707 | (L & n0702) | poc; |
wire n0659 = (clk2 & n0685) | (clk1 & L); |
wire n0676 = clk1 | n0685 | n0699; |
|
wire n0702 = ~clk2; |
reg n0685; |
reg n0699; |
reg n0707; |
always @(posedge sysclk) begin |
if (clk1) begin |
n0685 <= ~L; |
n0707 <= L; |
end |
if (n0702) |
n0699 <= ~L; |
end |
wire n0700 = n0707 | (L & n0702) | poc; |
wire n0659 = (clk2 & n0685) | (clk1 & L); |
wire n0676 = clk1 | n0685 | n0699; |
|
// Incoming data from the external pads |
reg [3:0] data_in; |
always @* begin |
if (n0659) data_in = 4'b1111; |
else if (n0676) data_in = 4'bzzzz; |
else if (poc) data_in = 4'b0000; |
else data_in = data_pad; |
end |
assign data = data_in; |
// Incoming data from the external pads |
reg [3:0] data_in; |
always @* begin |
if (n0659) data_in = 4'b1111; |
else if (n0676) data_in = 4'bzzzz; |
else if (poc) data_in = 4'b0000; |
else data_in = data_pad; |
end |
assign data = data_in; |
|
// Outgoing data to the external pads |
reg [3:0] data_out; |
always @(posedge sysclk) begin |
if (n0702) |
data_out <= data; |
end |
assign data_pad = poc ? 4'b0000 : (n0700 ? 4'bzzzz : data_out); |
// Outgoing data to the external pads |
reg [3:0] data_out; |
always @(posedge sysclk) begin |
if (n0702) |
data_out <= data; |
end |
assign data_pad = poc ? 4'b0000 : (n0700 ? 4'bzzzz : data_out); |
|
endmodule |
/rtl/verilog/i4004/timing_io_tb.v
0,0 → 1,156
`timescale 1ns / 1ps |
`default_nettype none |
//////////////////////////////////////////////////////////////////////// |
// |
// MCS-4 i4004 CPU Timing and I/O Interface testbench |
// |
// This testbench instantiates a timing_io module, and drives it |
// with the basic 8-subcycle (A1, A2, A3, M1, M2, X1, X2, X3) |
// instruction cycle to allow confirmation of the correct direction |
// of transfer through the system bus interface. |
// |
// 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 timing_io_tb; |
|
localparam SYSCLK_TCY = 20; // sysclk period in nanoseconds |
|
// Inputs |
reg sysclk; |
wire clk1_pad; |
wire clk2_pad; |
reg poc_pad = 1'b0; |
reg test_pad = 1'b0; |
reg ior = 1'b0; |
reg cmrom = 1'b0; |
reg cmram0 = 1'b0; |
reg cmram1 = 1'b0; |
reg cmram2 = 1'b0; |
reg cmram3 = 1'b0; |
|
// Outputs |
wire clk1; |
wire clk2; |
wire a12; |
wire a22; |
wire a32; |
wire m12; |
wire m22; |
wire x12; |
wire x22; |
wire x32; |
wire sync; |
wire gate; |
wire poc; |
wire n0432; |
wire cmrom_pad; |
wire cmram0_pad; |
wire cmram1_pad; |
wire cmram2_pad; |
wire cmram3_pad; |
|
// Bidirs |
wire [3:0] data; |
wire [3:0] data_pad; |
|
// Instantiate the Unit Under Test (UUT) |
timing_io uut ( |
.sysclk(sysclk), |
.clk1_pad(clk1_pad), |
.clk2_pad(clk2_pad), |
.poc_pad(poc_pad), |
.ior(ior), |
.clk1(clk1), |
.clk2(clk2), |
.a12(a12), |
.a22(a22), |
.a32(a32), |
.m12(m12), |
.m22(m22), |
.x12(x12), |
.x22(x22), |
.x32(x32), |
.sync(sync), |
.gate(gate), |
.poc(poc), |
.data(data), |
.data_pad(data_pad), |
.test_pad(test_pad), |
.n0432(n0432), |
.cmrom(cmrom), |
.cmrom_pad(cmrom_pad), |
.cmram0(cmram0), |
.cmram0_pad(cmram0_pad), |
.cmram1(cmram1), |
.cmram1_pad(cmram1_pad), |
.cmram2(cmram2), |
.cmram2_pad(cmram2_pad), |
.cmram3(cmram3), |
.cmram3_pad(cmram3_pad) |
); |
|
// Instantiate the 2-phase clock generator |
clockgen #( |
.SYSCLK_TCY(SYSCLK_TCY) |
) clockgen ( |
.sysclk(sysclk), |
.clk1(clk1_pad), |
.clk2(clk2_pad) |
); |
|
always begin |
sysclk = 1'b0; |
#(SYSCLK_TCY / 2); |
sysclk = 1'b1; |
#(SYSCLK_TCY / 2); |
end |
|
reg [3:0] data_out; |
always @* begin |
if (poc) |
data_out = 4'bzzzz; |
else begin |
(* PARALLEL_CASE *) |
case (1'b1) |
a12: data_out = 4'b0001; |
a22: data_out = 4'b0010; |
a32: data_out = 4'b0011; |
m12: data_out = 4'bzzzz; |
m22: data_out = 4'bzzzz; |
x12: data_out = 4'b0110; |
x22: data_out = 4'b0111; |
x32: data_out = 4'b1000; |
default: data_out = 4'bxxxx; |
endcase |
end |
end |
assign data = data_out; |
assign data_pad = 4'bzzzz; |
|
initial begin |
// Initialize Inputs |
poc_pad = 1'b1; |
|
// Wait 10000 ns for global reset to finish |
#10000; |
|
// Add stimulus here |
@(posedge m12); |
poc_pad = 1'b0; |
end |
|
endmodule |
|