Skip to content

Commit d435d84

Browse files
committed
add support for in/out GPIO pins on ULX3S
The ULX3S has 2x28 IO pins that can be used either individually or as differential pairs. Currently we're going to assume an individual setup (you can configure their behaviour in the .lpf file). The convention is to see them as gp[27] (GPIO positive) and gn[27] (GPIO negative) arrays; this is how they're numbered on the board itself. Note that positive and negative moniker are irrelevant in the current configuration as the ports aren't set up as differential pairs. To work with this convention, we split out the GPIO address space into 0x00XX and 0x02XX for setting in/output values of gp and gn pins respectively, and use 0x01XX and 0x3XX for setting the direction of the gn and gp pins.
1 parent 9a92f21 commit d435d84

File tree

2 files changed

+76
-17
lines changed

2 files changed

+76
-17
lines changed

j1b/ulx3s/README.md

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,57 @@ USB:
1414
Now you can use `shell.py` in the parent directory to attach to the Forth
1515
process over UART. The Verilator bootstrap process will also work for ULX3S.
1616

17-
Currently only J1b itself, UART, buttons and LEDs are supported. Support for
18-
low-hanging fruit like GPIOs should follow shortly.
17+
Currently the J1b itself, UART, buttons, LEDs and GPIO ports are supported.
1918

19+
PWR button resets the board.
2020

21-
PWR button resets the board
2221

2322
memory map:
2423

24+
0x00XX: gp GPIOs
25+
read/write
26+
either write bit to or read bit from address corresponding to the gp pin numbers
27+
on the board
28+
29+
0x01XX: gp GPIO in/out direction
30+
read/write
31+
set direction of corresponding gp pin; 0 = input (default), 1 = output
32+
33+
example:
34+
$001b io@ \ read gn[27]
35+
OUTPUT $011b io! \ set direction of gn[27] to output
36+
1 $001b io! \ set gn[27] to high
37+
38+
39+
0x02XX: gn GPIOs
40+
read/write
41+
either write bit to or read bit from address corresponding to the gn pin numbers
42+
on the board
43+
44+
0x03XX: gn GPIO in/out direction
45+
read/write
46+
set direction of corresponding gn pin; 0 = input (default), 1 = output
47+
48+
example:
49+
$021b io@ \ read gn[27]
50+
OUTPUT $031b io! \ set direction of gn[27] to output
51+
1 $021b io! \ set gn[27] to high
52+
53+
2554
0x0400: buttons (excluding PWR)
26-
direction: in
55+
read
2756
each button occupies one bit in the bottom 6 bits
2857

2958
as per silkscreen labels on the board:
3059
| 32 - 6 | 5 | 4 | 3 | 2 | 1 | 0 |
3160
unused RIGHT LEFT DOWN UP F2 F1
3261

3362
example:
34-
3563
$0400 io@ .
3664

3765

38-
3966
0x0404: leds
40-
direction: out
67+
write
4168
each led occupies one bit in the bottom 8 bits
4269

4370
as per silkscreen labels on the board:
@@ -53,4 +80,3 @@ example:
5380
again
5481
;
5582
led-counter
56-

j1b/verilog/ulx3s_top.v

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ module ulx3s_top(
55

66
input wire [6:0] btn,
77
output wire [7:0] led,
8+
output wire [3:0] audio_l, audio_r, audio_v,
9+
inout wire [27:0] gp, gn,
810

911
input wire ftdi_txd,
1012
output wire ftdi_rxd,
@@ -95,6 +97,11 @@ module ulx3s_top(
9597
{io_wr_, io_rd_, mem_addr_, dout_} <= {io_wr, io_rd, mem_addr, dout};
9698

9799
/* READ WRITE
100+
00xx GP rd GP wr
101+
01xx GP direction GP direction
102+
02xx GN rd GN wr
103+
03xx GP direction GN direction
104+
98105
0400 buttons rd
99106
0404 LEDs wr
100107
@@ -111,31 +118,57 @@ module ulx3s_top(
111118

112119
reg [63:0] counter_;
113120

121+
reg [27:0] gp_o;
122+
wire [27:0] gp_i;
123+
reg [27:0] gp_dir; // 1:output, 0:input
124+
reg [27:0] gn_o;
125+
wire [27:0] gn_i;
126+
reg [27:0] gn_dir; // 1:output, 0:input
127+
114128
always @(posedge fclk) begin
115129
casez (mem_addr)
116-
16'h0400: din <= {27'd0, btn[6:1]};
130+
16'h00??: din <= gp_i[mem_addr[6:0]];
131+
16'h01??: din <= gp_dir[mem_addr[6:0]];
132+
16'h02??: din <= gn_i[mem_addr[6:0]];
133+
16'h03??: din <= gn_dir[mem_addr[6:0]];
117134

118-
16'h1000: din <= {24'd0, uart0_data};
119-
16'h1008: din <= uart_baud;
120-
16'h2000: din <= {30'd0, uart0_valid, !uart0_busy};
135+
16'h0400: din <= {27'd0, btn[6:1]};
121136

122-
16'h1010: din <= MHZ * 1000000;
123-
16'h1014: din <= counter_[31:0];
124-
16'h1018: din <= counter_[63:32];
125-
16'h101c: din <= ms;
137+
16'h1000: din <= {24'd0, uart0_data};
138+
16'h1008: din <= uart_baud;
139+
16'h2000: din <= {30'd0, uart0_valid, !uart0_busy};
126140

127-
default: din <= 32'bx;
141+
16'h1010: din <= MHZ * 1000000;
142+
16'h1014: din <= counter_[31:0];
143+
16'h1018: din <= counter_[63:32];
144+
16'h101c: din <= ms;
145+
146+
default: din <= 32'bx;
128147
endcase
129148

130149
if (io_wr_) begin
131150
casez (mem_addr_)
151+
16'h00??: gp_o[mem_addr_[6:0]] <= dout_[0];
152+
16'h01??: gp_dir[mem_addr_[6:0]] <= dout_[0];
153+
16'h02??: gn_o[mem_addr_[6:0]] <= dout_[0];
154+
16'h03??: gn_dir[mem_addr_[6:0]] <= dout_[0];
155+
132156
16'h0404: led <= dout_;
157+
133158
16'h1008: uart_baud <= dout_;
134159
16'h1010: counter_ <= counter;
135160
endcase
136161
end
137162
end
138163

164+
genvar i;
165+
for (i = 0; i <= 27; i = i + 1) begin
166+
assign gp[i] = gp_dir[i] ? gp_o[i] : 1'bz;
167+
assign gp_i[i] = gp[i];
168+
assign gn[i] = gn_dir[i] ? gn_o[i] : 1'bz;
169+
assign gn_i[i] = gn[i];
170+
end
171+
139172
assign uart0_wr = io_wr_ & (mem_addr_ == 16'h1000);
140173
assign uart0_rd = io_rd_ & (mem_addr_ == 16'h1000);
141174

0 commit comments

Comments
 (0)