MCU Verilog
Since I’m starting to get ready to mount the MCU CPLD, I thought I’d post the Verilog. It’s pretty straightforward.
I made an error in my assumption of the flash programmer, though and I see I have to possibly change something. I was assuming that the flash device was using the rising edge of the WR line to write and starting to drive the data lines at the same as WR went low. This led to errors because it was actually using the falling edge of WR and so the data wasn’t properly set up.
According to the Z-80 data sheet, the data is set up and held for ~15 ns before and after the WR line low is low. So, I should be able to use the rising edge of WR without a problem.
1 // This CPLD meant for the Lattice M4A5 64/32 is a simple bank switching
2 // memory control unit. It has 8 registers:
3 // 00 - bank 0
4 // 01 - bank 1
5 // 02 - bank 2
6 // 03 - bank 3
7 // 04 - update 0
8 // 05 - update 1
9 // 06 - update 2
10 // 07 - update 3
11 //
12 // The banks are meant to substitute starting address line 14.
13 // The update registers can only be written to from bank 00 i.e. the lowest
14 // 16kb.
15 //
16 // The banks are updated from the update registers when HALT is called
17 // from bank 0. NMI immediately follows HALT in this circumstance
18 //
19
20 `define BANKTOP 4
21 `define IO_RANGE 7:3
22 `define IO_VALUE 5'b00000
23 `define ACTIVELOW 1'b0
24
25 module mcu (
26 input pin_CLK,
27 input [15:0] pins_A,
28 input pin_nRESET, pin_nWR, pin_nRD, pin_nMREQ, pin_nIORQ, pin_nM1, pin_nHALT,
29 output reg pin_nNMI,
30 output [`BANKTOP-1:0] pins_Aout,
31 output pin_nCS0, pin_nCS1,
32 inout [7:0] pins_D
33 );
34
35 reg [`BANKTOP:0] bankReg0;
36 reg [`BANKTOP:0] bankReg1;
37 reg [`BANKTOP:0] bankReg2;
38 reg [`BANKTOP:0] bankReg3;
39 reg [`BANKTOP:0] updateReg0;
40 reg [`BANKTOP:0] updateReg1;
41 reg [`BANKTOP:0] updateReg2;
42 reg [`BANKTOP:0] updateReg3;
43 reg nKernel;
44 wire nInRange;
45 reg [7:0] Dint;
46 wire [`BANKTOP:0] selBank;
47
48 // Determine if the address bus is talking to us
49 assign nInRange = ((pins_A[`IO_RANGE] == `IO_VALUE) ? 1'b0 : 1'b1) | pin_nIORQ;
50
51 // Get selected output
52 assign selBank = pins_A[15] ? (pins_A[14] ? bankReg3 : bankReg2) : (pins_A[14] ? bankReg1 : bankReg0);
53
54 // assign Aout -- Hi-Z on reset
55 assign pins_Aout = pin_nRESET ? selBank[`BANKTOP-1:0] : 8'bzzzzzzzz;
56
57 // CS's for top bit of bank
58 assign pin_nCS0 = pin_nRESET ? (selBank[`BANKTOP] | pin_nMREQ) : 1'bz;
59 assign pin_nCS1 = pin_nRESET ? ((~selBank[`BANKTOP]) | pin_nMREQ) : 1'bz;
60
61 assign pins_D = ((pin_nRD | nInRange) == 1'b0) ? Dint : 8'bzzzzzzzz;
62
63 always @(*) begin
64 case (pins_A[2:0])
65 3'b000:
66 Dint = bankReg0;
67 3'b001:
68 Dint = bankReg1;
69 3'b010:
70 Dint = bankReg2;
71 3'b011:
72 Dint = bankReg3;
73 3'b100:
74 Dint = updateReg0;
75 3'b101:
76 Dint = updateReg1;
77 3'b110:
78 Dint = updateReg2;
79 3'b111:
80 Dint = updateReg3;
81 endcase
82 end
83
84 always @(posedge pin_CLK) begin
85 if (pin_nRESET == `ACTIVELOW) begin
86 bankReg0 <= 0;
87 bankReg1 <= 1;
88 bankReg2 <= (1 << `BANKTOP);
89 bankReg3 <= (1 << `BANKTOP) | 1;
90 pin_nNMI <= 1'b1;
91 end
92 else begin
93 if ((pin_nHALT | nKernel) == `ACTIVELOW) begin
94 bankReg0 <= updateReg0;
95 bankReg1 <= updateReg1;
96 bankReg2 <= updateReg2;
97 bankReg3 <= updateReg3;
98 end
99
100 pin_nNMI <= pin_nHALT;
101
102 if ((pin_nM1 | pin_nMREQ) == `ACTIVELOW) begin
103 nKernel <= pins_A[15] | pins_A[14];
104 end
105 end
106 end
107
108 always @(posedge pin_nWR) begin
109 if (((pin_nIORQ | nInRange) == `ACTIVELOW) && (pins_A[2] == 1'b1)) begin
110 case (pins_A[1:0])
111 2'b00: begin
112 updateReg0 <= pins_D;
113 end
114 2'b01: begin
115 updateReg1 <= pins_D;
116 end
117 2'b10: begin
118 updateReg2 <= pins_D;
119 end
120 2'b11: begin
121 updateReg3 <= pins_D;
122 end
123 endcase
124 end
125 end
126
127 endmodule
So, this is already old. I added a BUSACK pin for better cooperation with the in circuit flash programmer. Also added it to the testbench. It seems to work in the simulation.