Skip to content

Commit 0decdb4

Browse files
committed
Merge branch 'gamepad_pmod' into ihp130
2 parents 1cc858c + 72b34f7 commit 0decdb4

File tree

6 files changed

+404
-34
lines changed

6 files changed

+404
-34
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Atari 2600 fits this goal perfectly thanks to it is minimal hardware design (i.e
1616

1717
The design is so small that around **40** Atari consoles fit on a silicon die! The silicon area of this design is 0.2 mm2 (1000um x 216um) on a SkyWater 130 nm node.
1818

19-
Explore chip in [3D directly in the browser](https://gds-viewer.tinytapeout.com/?model=https://rejunity.github.io/tt09-atari-2600/tinytapeout.gds.gltf)!
19+
Explore chip in [3D directly in the browser](https://gds-viewer.tinytapeout.com/?model=https://rejunity.github.io/tiny-atari-2600/tinytapeout.gds.gltf)!
2020

2121
This design is submitted for fabrication with [Tiny Tapeout 09](https://tinytapeout.com) via eFabless [CI 2411 Shuttle](https://platform.efabless.com/shuttles/CI%202411). Check status here: https://app.tinytapeout.com/projects/1343
2222

info.yaml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ project:
2121
- "vga_hvsync_generator.v"
2222
- "palette_24bpp.v"
2323
- "qspi_flash.v"
24+
- "gamepad_pmod.v"
2425
- "atari2600.v"
2526
- "6502.v"
2627
- "ALU.v"
@@ -30,14 +31,14 @@ project:
3031
# The pinout of your project. Leave unused pins blank. DO NOT delete or add any pins.
3132
pinout:
3233
# Inputs
33-
ui[0]: "UP / Switch 1"
34-
ui[1]: "DOWN / Switch 2"
35-
ui[2]: "LEFT / Switch BW"
36-
ui[3]: "RIGHT / Switch 3"
37-
ui[4]: "FIRE"
38-
ui[5]: "Joystick 1 / 2"
39-
ui[6]: "Switches"
40-
ui[7]: "RESET"
34+
ui[0]: "UP / Difficulty Switch P1"
35+
ui[1]: "DOWN / Difficulty Switch P2"
36+
ui[2]: "LEFT / Monochrome Switch"
37+
ui[3]: "RIGHT"
38+
ui[4]: "FIRE / Gamepad LATCH"
39+
ui[5]: "SELECT / Gamepad CLK"
40+
ui[6]: "Switches / Gamepad DATA"
41+
ui[7]: "START"
4142

4243
# Outputs
4344
uo[0]: "R1"

src/PIA.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ module pia (
5555
case (adr_i)
5656
7'h00: begin dat_o <= {buttons[6:3], buttons[6:3]}; end// SWCHA
5757
7'h01: dat_o <= swa_dir; // SWACNT
58-
7'h02: dat_o <= {~sw[0], ~sw[1], 2'b11, sw[2], 1'b1, buttons[SELECT], buttons[RESET]}; // SWCHB
58+
7'h02: dat_o <= {~sw[1], ~sw[0], 2'b11, sw[2], 1'b1, buttons[SELECT], buttons[RESET]}; // SWCHB
5959
7'h03: dat_o <= {2'b0, swb_dir[5:4], 1'b0, swb_dir[2], 2'b0}; // SWBCNT
6060
7'h04: begin; dat_o <= intim; underflow <= 0; end // INTIM
6161
7'h05: begin dat_o <= {instat, 6'b0}; instat[0] <= 0; end// INSTAT

src/gamepad_pmod.v

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
/*
2+
* Copyright (c) 2025 Pat Deegan, https://psychogenic.com
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Version: 1.0.0
5+
*
6+
* Interfacing code for the Gamepad Pmod from Psycogenic Technologies,
7+
* designed for Tiny Tapeout.
8+
*
9+
* There are two high-level modules that most users will be interested in:
10+
* - gamepad_pmod_single: for a single controller;
11+
* - gamepad_pmod_dual: for two controllers.
12+
*
13+
* There are also two lower-level modules that you can use if you want to
14+
* handle the interfacing yourself:
15+
* - gamepad_pmod_driver: interfaces with the Pmod and provides the raw data;
16+
* - gamepad_pmod_decoder: decodes the raw data into button states.
17+
*
18+
* The docs, schematics, PCB files, and firmware code for the Gamepad Pmod
19+
* are available at https://github.com/psychogenic/gamepad-pmod.
20+
*/
21+
22+
/**
23+
* gamepad_pmod_driver -- Serial interface for the Gamepad Pmod.
24+
*
25+
* This module reads raw data from the Gamepad Pmod *serially*
26+
* and stores it in a shift register. When the latch signal is received,
27+
* the data is transferred into `data_reg` for further processing.
28+
*
29+
* Functionality:
30+
* - Synchronizes the `pmod_data`, `pmod_clk`, and `pmod_latch` signals
31+
* to the system clock domain.
32+
* - Captures serial data on each falling edge of `pmod_clk`.
33+
* - Transfers the shifted data into `data_reg` when `pmod_latch` goes low.
34+
*
35+
* Parameters:
36+
* - `BIT_WIDTH`: Defines the width of `data_reg` (default: 24 bits).
37+
*
38+
* Inputs:
39+
* - `rst_n`: Active-low reset.
40+
* - `clk`: System clock.
41+
* - `pmod_data`: Serial data input from the Pmod.
42+
* - `pmod_clk`: Serial clock from the Pmod.
43+
* - `pmod_latch`: Latch signal indicating the end of data transmission.
44+
*
45+
* Outputs:
46+
* - `data_reg`: Captured parallel data after shifting is complete.
47+
*/
48+
module gamepad_pmod_driver #(
49+
parameter BIT_WIDTH = 24
50+
) (
51+
input wire rst_n,
52+
input wire clk,
53+
input wire pmod_data,
54+
input wire pmod_clk,
55+
input wire pmod_latch,
56+
output reg [BIT_WIDTH-1:0] data_reg
57+
);
58+
59+
reg pmod_clk_prev;
60+
reg pmod_latch_prev;
61+
reg [BIT_WIDTH-1:0] shift_reg;
62+
63+
// Sync Pmod signals to the clk domain:
64+
reg [1:0] pmod_data_sync;
65+
reg [1:0] pmod_clk_sync;
66+
reg [1:0] pmod_latch_sync;
67+
68+
always @(posedge clk) begin
69+
if (~rst_n) begin
70+
pmod_data_sync <= 2'b0;
71+
pmod_clk_sync <= 2'b0;
72+
pmod_latch_sync <= 2'b0;
73+
end else begin
74+
pmod_data_sync <= {pmod_data_sync[0], pmod_data};
75+
pmod_clk_sync <= {pmod_clk_sync[0], pmod_clk};
76+
pmod_latch_sync <= {pmod_latch_sync[0], pmod_latch};
77+
end
78+
end
79+
80+
always @(posedge clk) begin
81+
if (~rst_n) begin
82+
/* Initialize data and shift registers to all 1s so they're detected as "not present".
83+
* This accounts for cases where we have:
84+
* - setup for 2 controllers;
85+
* - only a single controller is connected; and
86+
* - the driver in those cases only sends bits for a single controller.
87+
*/
88+
data_reg <= {BIT_WIDTH{1'b1}};
89+
shift_reg <= {BIT_WIDTH{1'b1}};
90+
pmod_clk_prev <= 1'b0;
91+
pmod_latch_prev <= 1'b0;
92+
end
93+
begin
94+
pmod_clk_prev <= pmod_clk_sync[1];
95+
pmod_latch_prev <= pmod_latch_sync[1];
96+
97+
// Capture data on rising edge of pmod_latch:
98+
if (pmod_latch_sync[1] & ~pmod_latch_prev) begin
99+
data_reg <= shift_reg;
100+
end
101+
102+
// Sample data on rising edge of pmod_clk:
103+
if (pmod_clk_sync[1] & ~pmod_clk_prev) begin
104+
shift_reg <= {shift_reg[BIT_WIDTH-2:0], pmod_data_sync[1]};
105+
end
106+
end
107+
end
108+
109+
endmodule
110+
111+
112+
/**
113+
* gamepad_pmod_decoder -- Decodes raw data from the Gamepad Pmod.
114+
*
115+
* This module takes a 12-bit parallel data register (`data_reg`)
116+
* and decodes it into individual button states. It also determines
117+
* whether a controller is connected.
118+
*
119+
* Functionality:
120+
* - If `data_reg` contains all `1's` (`0xFFF`), it indicates that no controller is connected.
121+
* - Otherwise, it extracts individual button states from `data_reg`.
122+
*
123+
* Inputs:
124+
* - `data_reg [11:0]`: Captured button state data from the gamepad.
125+
*
126+
* Outputs:
127+
* - `b, y, select, start, up, down, left, right, a, x, l, r`: Individual button states (`1` = pressed, `0` = released).
128+
* - `is_present`: Indicates whether a controller is connected (`1` = connected, `0` = not connected).
129+
*/
130+
module gamepad_pmod_decoder (
131+
input wire [11:0] data_reg,
132+
output wire b,
133+
output wire y,
134+
output wire select,
135+
output wire start,
136+
output wire up,
137+
output wire down,
138+
output wire left,
139+
output wire right,
140+
output wire a,
141+
output wire x,
142+
output wire l,
143+
output wire r,
144+
output wire is_present
145+
);
146+
147+
// When the controller is not connected, the data register will be all 1's
148+
wire reg_empty = (data_reg == 12'hfff);
149+
assign is_present = reg_empty ? 0 : 1'b1;
150+
assign {b, y, select, start, up, down, left, right, a, x, l, r} = reg_empty ? 0 : data_reg;
151+
152+
endmodule
153+
154+
155+
/**
156+
* gamepad_pmod_single -- Main interface for a single Gamepad Pmod controller.
157+
*
158+
* This module provides button states for a **single controller**, reducing
159+
* resource usage (fewer flip-flops) compared to a dual-controller version.
160+
*
161+
* Inputs:
162+
* - `pmod_data`, `pmod_clk`, and `pmod_latch` are the signals from the PMOD interface.
163+
*
164+
* Outputs:
165+
* - Each button's state is provided as a single-bit wire (e.g., `start`, `up`, etc.).
166+
* - `is_present` indicates whether the controller is connected (`1` = connected, `0` = not detected).
167+
*/
168+
module gamepad_pmod_single (
169+
input wire rst_n,
170+
input wire clk,
171+
input wire pmod_data,
172+
input wire pmod_clk,
173+
input wire pmod_latch,
174+
175+
output wire b,
176+
output wire y,
177+
output wire select,
178+
output wire start,
179+
output wire up,
180+
output wire down,
181+
output wire left,
182+
output wire right,
183+
output wire a,
184+
output wire x,
185+
output wire l,
186+
output wire r,
187+
output wire is_present
188+
);
189+
190+
wire [11:0] gamepad_pmod_data;
191+
192+
gamepad_pmod_driver #(
193+
.BIT_WIDTH(12)
194+
) driver (
195+
.rst_n(rst_n),
196+
.clk(clk),
197+
.pmod_data(pmod_data),
198+
.pmod_clk(pmod_clk),
199+
.pmod_latch(pmod_latch),
200+
.data_reg(gamepad_pmod_data)
201+
);
202+
203+
gamepad_pmod_decoder decoder (
204+
.data_reg(gamepad_pmod_data),
205+
.b(b),
206+
.y(y),
207+
.select(select),
208+
.start(start),
209+
.up(up),
210+
.down(down),
211+
.left(left),
212+
.right(right),
213+
.a(a),
214+
.x(x),
215+
.l(l),
216+
.r(r),
217+
.is_present(is_present)
218+
);
219+
220+
endmodule
221+
222+
223+
/**
224+
* gamepad_pmod_dual -- Main interface for the Pmod gamepad.
225+
* This module provides button states for two controllers using
226+
* 2-bit vectors for each button (e.g., start[1:0], up[1:0], etc.).
227+
*
228+
* Each button state is represented as a 2-bit vector:
229+
* - Index 0 corresponds to the first controller (e.g., up[0], y[0], etc.).
230+
* - Index 1 corresponds to the second controller (e.g., up[1], y[1], etc.).
231+
*
232+
* The `is_present` signal indicates whether a controller is connected:
233+
* - `is_present[0] == 1` when the first controller is connected.
234+
* - `is_present[1] == 1` when the second controller is connected.
235+
*
236+
* Inputs:
237+
* - `pmod_data`, `pmod_clk`, and `pmod_latch` are the 3 wires coming from the Pmod interface.
238+
*
239+
* Outputs:
240+
* - Button state vectors for each controller.
241+
* - Presence detection via `is_present`.
242+
*/
243+
module gamepad_pmod_dual (
244+
input wire rst_n,
245+
input wire clk,
246+
input wire pmod_data,
247+
input wire pmod_clk,
248+
input wire pmod_latch,
249+
250+
output wire [1:0] b,
251+
output wire [1:0] y,
252+
output wire [1:0] select,
253+
output wire [1:0] start,
254+
output wire [1:0] up,
255+
output wire [1:0] down,
256+
output wire [1:0] left,
257+
output wire [1:0] right,
258+
output wire [1:0] a,
259+
output wire [1:0] x,
260+
output wire [1:0] l,
261+
output wire [1:0] r,
262+
output wire [1:0] is_present
263+
);
264+
265+
wire [23:0] gamepad_pmod_data;
266+
267+
gamepad_pmod_driver driver (
268+
.rst_n(rst_n),
269+
.clk(clk),
270+
.pmod_data(pmod_data),
271+
.pmod_clk(pmod_clk),
272+
.pmod_latch(pmod_latch),
273+
.data_reg(gamepad_pmod_data)
274+
);
275+
276+
gamepad_pmod_decoder decoder1 (
277+
.data_reg(gamepad_pmod_data[11:0]),
278+
.b(b[0]),
279+
.y(y[0]),
280+
.select(select[0]),
281+
.start(start[0]),
282+
.up(up[0]),
283+
.down(down[0]),
284+
.left(left[0]),
285+
.right(right[0]),
286+
.a(a[0]),
287+
.x(x[0]),
288+
.l(l[0]),
289+
.r(r[0]),
290+
.is_present(is_present[0])
291+
);
292+
293+
gamepad_pmod_decoder decoder2 (
294+
.data_reg(gamepad_pmod_data[23:12]),
295+
.b(b[1]),
296+
.y(y[1]),
297+
.select(select[1]),
298+
.start(start[1]),
299+
.up(up[1]),
300+
.down(down[1]),
301+
.left(left[1]),
302+
.right(right[1]),
303+
.a(a[1]),
304+
.x(x[1]),
305+
.l(l[1]),
306+
.r(r[1]),
307+
.is_present(is_present[1])
308+
);
309+
310+
endmodule

0 commit comments

Comments
 (0)