verilog-apple-one/rtl/led_and_key/tm1638.v
2018-01-27 00:21:05 +11:00

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