mirror of
https://github.com/alangarf/apple-one.git
synced 2024-08-29 20:29:32 +00:00
123 lines
3.2 KiB
Verilog
123 lines
3.2 KiB
Verilog
module tm1638(
|
|
input clk,
|
|
input clk_en,
|
|
input rst,
|
|
|
|
input data_latch,
|
|
inout [7:0] data,
|
|
input rw,
|
|
|
|
output busy,
|
|
|
|
output sclk,
|
|
input dio_in,
|
|
output reg dio_out
|
|
);
|
|
|
|
localparam CLK_DIV = 3; // seems happy at 12MHz with 3
|
|
localparam CLK_DIV1 = CLK_DIV - 1;
|
|
localparam [1:0]
|
|
S_IDLE = 2'h0,
|
|
S_WAIT = 2'h1,
|
|
S_TRANSFER = 2'h2;
|
|
|
|
reg [1:0] cur_state, next_state;
|
|
reg [CLK_DIV1:0] sclk_d, sclk_q;
|
|
reg [7:0] data_d, data_q, data_out_d, data_out_q;
|
|
reg dio_out_d;
|
|
reg [2:0] ctr_d, ctr_q;
|
|
|
|
// output read data if we're reading
|
|
assign data = rw ? 8'hZZ : data_out_q;
|
|
|
|
// we're busy if we're not idle
|
|
assign busy = cur_state != S_IDLE;
|
|
|
|
// tick the clock if we're transfering data
|
|
assign sclk = ~((~sclk_q[CLK_DIV1]) & (cur_state == S_TRANSFER));
|
|
|
|
always @(*)
|
|
begin
|
|
sclk_d = sclk_q;
|
|
data_d = data_q;
|
|
dio_out_d = dio_out;
|
|
ctr_d = ctr_q;
|
|
data_out_d = data_out_q;
|
|
next_state = cur_state;
|
|
|
|
case(cur_state)
|
|
S_IDLE: begin
|
|
sclk_d = 0;
|
|
if (data_latch) begin
|
|
// if we're reading, set to zero, otherwise latch in
|
|
// data to send
|
|
data_d = rw ? data : 8'b0;
|
|
next_state = S_WAIT;
|
|
end
|
|
end
|
|
|
|
S_WAIT: begin
|
|
sclk_d = sclk_q + 1;
|
|
// wait till we're halfway into clock pulse
|
|
if (sclk_q == {1'b0, {CLK_DIV1{1'b1}}}) begin
|
|
sclk_d = 0;
|
|
next_state = S_TRANSFER;
|
|
end
|
|
end
|
|
|
|
S_TRANSFER: begin
|
|
sclk_d = sclk_q + 1;
|
|
if (sclk_q == 0) begin
|
|
// start of clock pulse, output MSB
|
|
dio_out_d = data_q[0];
|
|
|
|
end else if (sclk_q == {1'b0, {CLK_DIV1{1'b1}}}) begin
|
|
// halfway through pulse, read from device
|
|
data_d = {dio_in, data_q[7:1]};
|
|
|
|
end else if (&sclk_q) begin
|
|
// end of pulse, tick the counter
|
|
ctr_d = ctr_q + 1;
|
|
|
|
if (&ctr_q) begin
|
|
// last bit sent, switch back to idle
|
|
// and output any data recieved
|
|
next_state = S_IDLE;
|
|
data_out_d = data_q;
|
|
|
|
dio_out_d = 0;
|
|
end
|
|
end
|
|
end
|
|
|
|
default:
|
|
next_state = S_IDLE;
|
|
endcase
|
|
end
|
|
|
|
always @(posedge clk)
|
|
begin
|
|
if (clk_en)
|
|
begin
|
|
if (rst)
|
|
begin
|
|
cur_state <= S_IDLE;
|
|
sclk_q <= 0;
|
|
ctr_q <= 0;
|
|
dio_out <= 0;
|
|
data_q <= 0;
|
|
data_out_q <= 0;
|
|
end
|
|
else
|
|
begin
|
|
cur_state <= next_state;
|
|
sclk_q <= sclk_d;
|
|
ctr_q <= ctr_d;
|
|
dio_out <= dio_out_d;
|
|
data_q <= data_d;
|
|
data_out_q <= data_out_d;
|
|
end
|
|
end
|
|
end
|
|
endmodule
|