GPIO SPI CPLD
In order to connect the UART (MAX3110E) part to the Z-80, I’ll use a CPLD that does SPI. My first CPLD plan was fairly specific to the SPI of the UART part. But, it makes more sense to make something a little more general purpose. So, I came up with the following verilog design.
This design should allow me to do not just SPI but also I2C. And it has 8 GPIO.
The MAX3110E requires that its CS goes low while SCLK is low. Then a 16 bit data exchange takes place. Then while SCLK is low, CS goes high. It updates Dout and expects Din to be updated on SCLK falling edge and clocks in and expects the user to clock in on the rising edge. Sixteen times.
So this will be able to do it by using a GPIO as CE.
Here some example Z-80 assembly that would use the CPLD to communicate with the MAX3110E:
1 ; Assume HL is the data to write out to MAX3110E and HL will have the
2 ; exchanged MAX3110E data upon return
3 ; CS# low
4 in a, (#0x80) ; get current GPIOs
5 and a, ~0x01 ; set bit 0 to 0
6 out (#0x80), a ; write GPIO back out
7
8 ; exchange H
9 out (#0x83), h ; load shift register
10 out (#0x84), #0x48 ; shift 8 toward MSB
11 nop ; give it time to work
12 nop
13 in h, (#0x83) ; read in first 8 bits from MAX3110E
14
15 ; exchange L
16 out (#0x83), l ; load shift register
17 out (#0x84), #0x48 ; shift 8 toward MSB
18 nop ; give it time to work
19 nop
20 in l, (#0x83) ; read in last 8 bits from MAX3110E
21
22 ; CS# high
23 in a, (#0x80) ; get current GPIOs
24 or a, #0x01 ; set bit 0 to 0
25 out (#0x80), a ; write GPIO back out
26
27 ret
Here is the verilog:
1 // SPI module
2 //
3 // This CPLD will provide 8 GPIO and limited serial IO for SPI and/or I2C
4 // The registers are:
5 // 0x80: GPIO outvalues
6 // A read/write register holding values to be written on GPIO lines
7 // that are in output mode
8 // 0x81: GPIO direction (1=out, 0=in)
9 // A read/write register to control the input/output mode of the GPIO
10 // lines
11 // 0x82: GPIO levels
12 // A read only register with the current GPIO levels.
13 // 0x83: shift register (shifted based on control register)
14 // A read/write register with data to be shifted in/out
15 // 0x84: shift control
16 // bits 3-0: count
17 // 0: no shifting
18 // 1-8: shift by the amount
19 // 9-15: not supported
20 // bit 4: 1=HiZ Dout and use it for input, 0=use Din for input
21 // Use 1 for I2C type buses where the clock master controls
22 // a bidirectional data line
23 // Use 0 for full duplex buses with separate Din and Dout
24 // bit 5: 1=shift toward MSB 0=SCK shift toward LSB
25 // Use 1 to shift up. If the count is less than 8, the value
26 // written to the shift register must be shifted by the CPU
27 // Data is read into the LSB
28 // Use 0 to shift down. Data is read into the MSB
29 // bit 6: 1=SCK goes write high read low 0=SCK goes write low read high
30 // Internally, the counter is decremented every 2 pin_CLK. The
31 // first beat outputs Dout. The second shifts Din in.
32 // Use 0 if the first beat is 0 and the second is 1. i.e. read on
33 // rising edge
34 // Use 1 if the first beat is 1 and the second is 0. i.e. read on
35 // falling edge
36 // bit 7: 1=SCK default high, 0=SCK default low
37 // Use this bit to set the SCK before and after the actual
38 // shift operation
39 //
40 // Usage:
41 // 0x80, 0x81, and 0x82 control 8 GPIO pins
42 // read and write to 0x83 for data shifted in/out
43 // Use 0x84 to write a write only count. This will immediately start
44 // to shift the shift register in/out. Use a NOP or two to ensure it
45 // is complete before reading or writing
46 // Use bit5 =1 for 2 wire serial and =0 for 3 wire serial.
47 //
48 `define IO_VALUE 5'b10000
49 `define IO_RANGE 7:3
50 `define ACTIVELOW 1'b0
51
52 module GPIO_SPI(
53 // 2MHz clock for CPU
54 input pin_CLK,
55
56 // RESET
57 input pin_nRESET,
58
59 // CPU ddress bus
60 input [7:0] pins_A,
61
62 // CPU data bus
63 inout [7:0] pins_D,
64
65 // CPU pins
66 input pin_nIORQ,
67 input pin_nRD,
68 input pin_nWR,
69
70 // Pins to UART
71 input pin_Din,
72 inout pin_Dout,
73 output reg pin_SCK,
74
75 // GPIO
76 inout [7:0]pins_GPIO
77 );
78
79 // registers
80 reg [7:0] regGpioOutLevel;
81 reg [7:0] regGpioDirection;
82 reg [7:0] regShift;
83 reg [3:0] regCounter;
84 reg regDoutIsDin;
85 reg regShiftTowardMSB;
86 reg regSckHigh;
87 reg regDefaultSCK;
88
89 // shift
90 reg sck;
91 reg Dout;
92 wire Din;
93
94 // Internal databus for output
95 reg [7:0] Dint;
96
97 // IO request in the addressable range of this module
98 wire nInRange;
99
100 assign Din = regDoutIsDin ? pin_Dout : pin_Din;
101 assign pin_Dout = regDoutIsDin ? 1'bz : Dout;
102
103 // An IO read in the addressable range of this module
104 assign nInRange = ((pins_A[`IO_RANGE] == `IO_VALUE)? 1'b0 : 1'b1) | pin_nIORQ;
105
106 // Bidirectional databus control
107 assign pins_D = ((pin_nRD | nInRange) == `ACTIVELOW) ? Dint : 8'bzzzzzzzz;
108
109 assign pins_GPIO[0] = regGpioDirection[0] ? regGpioOutLevel[0] : 1'bz;
110 assign pins_GPIO[1] = regGpioDirection[1] ? regGpioOutLevel[1] : 1'bz;
111 assign pins_GPIO[2] = regGpioDirection[2] ? regGpioOutLevel[2] : 1'bz;
112 assign pins_GPIO[3] = regGpioDirection[3] ? regGpioOutLevel[3] : 1'bz;
113 assign pins_GPIO[4] = regGpioDirection[4] ? regGpioOutLevel[4] : 1'bz;
114 assign pins_GPIO[5] = regGpioDirection[5] ? regGpioOutLevel[5] : 1'bz;
115 assign pins_GPIO[6] = regGpioDirection[6] ? regGpioOutLevel[6] : 1'bz;
116 assign pins_GPIO[7] = regGpioDirection[7] ? regGpioOutLevel[7] : 1'bz;
117
118
119 // Keep internal databus updated
120 always @(*) begin
121 case (pins_A[2:0])
122 3'b000: begin
123 Dint <= regGpioOutLevel[7:0];
124 end
125 3'b001: begin
126 Dint <= regGpioDirection[7:0];
127 end
128 3'b010: begin
129 Dint <= pins_GPIO[7:0];
130 end
131 3'b011: begin
132 Dint <= regShift[7:0];
133 end
134 default: begin
135 Dint <= 8'bxxxxxxxx;
136 end
137 endcase
138 end
139
140 always @(posedge pin_CLK) begin
141
142 if (pin_nRESET == `ACTIVELOW) begin
143 regGpioDirection <= 0;
144 regCounter <= 0;
145 regDoutIsDin <= 0;
146 regShiftTowardMSB <= 0;
147 regSckHigh <= 0;
148 sck <= 0;
149 end
150 else begin
151 if ((nInRange | pin_nWR) == `ACTIVELOW) begin
152 case (pins_A[2:0])
153 3'b000: begin
154 regGpioOutLevel[7:0] <= pins_D[7:0];
155 end
156 3'b001: begin
157 regGpioDirection[7:0] <= pins_D[7:0];
158 end
159 3'b011: begin
160 regShift[7:0] <= pins_D[7:0];
161 end
162 3'b100: begin
163 regCounter[3:0] <= pins_D[3:0];
164 regDoutIsDin <= pins_D[4];
165 regShiftTowardMSB <= pins_D[5];
166 regSckHigh <= pins_D[6];
167 regDefaultSCK = pins_D[7];
168 sck <= 1'b0;
169 end
170 endcase
171 pin_SCK <= regDefaultSCK;
172 end
173 else if ((|regCounter) == 1'b1) begin
174 if (sck == 1'b0) begin
175 Dout <= regShiftTowardMSB ? regShift[7] : regShift[0];
176 end
177 else begin
178 regCounter <= regCounter - 1;
179 if (regShiftTowardMSB) begin
180 regShift <= { regShift[6:0], Din };
181 end
182 else begin
183 regShift <= { Din, regShift[7:1] };
184 end
185 end
186 sck <= ~sck;
187 pin_SCK <= sck ^ regSckHigh;
188 end
189 else begin
190 pin_SCK <= regDefaultSCK;
191 end
192 end
193 end
194
195 endmodule