From ffba35f814eda0a4c47af601206cf2d3ab6eab03 Mon Sep 17 00:00:00 2001 From: Marius Gavrilescu Date: Tue, 16 Apr 2019 18:44:02 +0300 Subject: [PATCH] Implement a tiny part of i2c --- Makefile | 8 +-- i2c.v | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++++ master.pcf | 15 ++++++ master.v | 44 ++++++++++++++-- 4 files changed, 202 insertions(+), 9 deletions(-) create mode 100644 i2c.v create mode 100644 master.pcf diff --git a/Makefile b/Makefile index 0fb0a57..e2c55e5 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ -PROJ = toplevel -PIN_DEF = toplevel.pcf +PROJ = master +PIN_DEF = master.pcf DEVICE = hx1k all: $(PROJ).rpt $(PROJ).bin %.blif: %.v - tools/yosys/yosys -p 'synth_ice40 -top toplevel -blif $@' $< + tools/yosys/yosys -p 'synth_ice40 -top master -blif $@' $< %.asc: $(PIN_DEF) %.blif tools/arachne-pnr/bin/arachne-pnr -d $(subst hx,,$(subst lp,,$(DEVICE))) -o $@ -p $^ -P tq144 @@ -24,6 +24,6 @@ clean: sim: - tools/yosys/yosys -p 'read_verilog -sv -DSIM toplevel.v; prep -top toplevel -nordff; sim -clock CLKin -vcd test.vcd -n 3000' + tools/yosys/yosys -p 'read_verilog -sv -DSIM master.v; prep -top master -nordff; sim -clock CLKin -vcd test.vcd -n 3000' .PHONY: all prog clean sim diff --git a/i2c.v b/i2c.v new file mode 100644 index 0000000..6815617 --- /dev/null +++ b/i2c.v @@ -0,0 +1,144 @@ +module i2c_write ( + input clk, + output scl, + output sda, + input [7:0] tx_byte, + input transmit, + input [5:0] more_bytes, /* 0 or 1 or 16 are reasonable */ + input [7:0] mb_in, + output [5:0] mb_addr, + output is_transmitting); + +parameter CLOCK_DIVIDE = 1; + +// States for the transmitting state machine. +// We transmit a START, then the address (constant 0xE1), then +// [tx_byte], then [more_bytes] bytes from [mb_in] +parameter TX_IDLE = 0; +parameter TX_ADDRESS = 1; +parameter TX_FIRST_BYTE = 2; +parameter TX_MORE_BYTES = 3; +parameter TX_STOP = 4; + +reg [10:0] tx_clk_divider = CLOCK_DIVIDE; + +reg data_out = 1'b1; +reg clk_out = 1'b1; + +reg [3:0] tx_state = TX_IDLE; +reg [5:0] tx_countdown; +reg [3:0] tx_bits_remaining; +reg [7:0] tx_data; + +reg [3:0] step = 0; + +wire [7:0] address = {7'h70, 1'b0}; // address 0x70, write +wire [15:0] address_data = {address, tx_data}; + +assign sda = data_out; +assign scl = clk_out; +assign is_transmitting = tx_state != TX_IDLE; + +always @(posedge clk) begin + // The clk_divider counter counts down from + // the CLOCK_DIVIDE constant. Whenever it + // reaches 0, 1/16 of the bit period has elapsed. + // Countdown timers for the receiving and transmitting + // state machines are decremented. + tx_clk_divider = tx_clk_divider - 1; + if (!tx_clk_divider) begin + tx_clk_divider = CLOCK_DIVIDE; + tx_countdown = tx_countdown - 1; + end + + // Transmit state machine + case (tx_state) + TX_IDLE: begin + if (transmit) begin + // If the transmit flag is raised in the idle + // state, save tx_byte for transmission + tx_data = tx_byte; + // Send the initial, low pulse of 1 bit period + // to signal the start, followed by the data + data_out = 0; + tx_state = TX_ADDRESS; + tx_bits_remaining = 8; + step = 0; + end + end // case: TX_IDLE + + TX_ADDRESS: begin + if(step == 0) begin + clk_out <= 0; + step <= 1; + end else if (tx_bits_remaining == 0) begin + if(step == 1) begin + data_out <= 0; // really should be z, not 0 + step <= 2; + end else if(step == 2)begin + clk_out <= 1; + step <= 3; + end else begin + step <= 0; + + tx_state <= TX_FIRST_BYTE; + tx_bits_remaining <= 8; + end + end else if(step == 1) begin + data_out <= address[tx_bits_remaining - 1]; + step <= 2; + end else if(step == 2) begin + clk_out <= 1; + step <= 3; + end else begin // step == 3 + tx_bits_remaining = tx_bits_remaining - 1; + step <= 0; + end + end // case: TX_ADDRESS + + TX_FIRST_BYTE: begin + if(step == 0) begin + clk_out <= 0; + step <= 1; + end else if (tx_bits_remaining == 0) begin + if(step == 1) begin + data_out <= 0; // really should be z, not 0 + step <= 2; + end else if(step == 2)begin + clk_out <= 1; + step <= 3; + end else begin + step <= 0; + + tx_state <= TX_STOP; + end + end else if(step == 1) begin + data_out <= tx_data[tx_bits_remaining - 1]; + step <= 2; + end else if(step == 2) begin + clk_out <= 1; + step <= 3; + end else begin // step == 3 + tx_bits_remaining = tx_bits_remaining - 1; + step <= 0; + end + end // case: TX_FIRST_BYTE + + TX_STOP: begin + if(step == 0) begin + clk_out <= 0; + step <= 1; + end else if(step == 1) begin + step <= 2; + end else if(step == 2) begin + clk_out <= 1; + step <= 3; + end else begin + data_out <= 1; + step <= 0; + tx_state <= TX_IDLE; + end + end + endcase +end +endmodule // i2c_write diff --git a/master.pcf b/master.pcf new file mode 100644 index 0000000..037381f --- /dev/null +++ b/master.pcf @@ -0,0 +1,15 @@ +# Red LEDs +set_io led[0] 99 +set_io led[1] 98 +set_io led[2] 97 +set_io led[3] 96 + +# Green LED +set_io led[4] 95 + +# 12 MHz clock +set_io CLKin 21 + +# i2c to led matrix +set_io scl 80 # PIO1_04 +set_io sda 81 # PIO1_05 diff --git a/master.v b/master.v index b54abe9..f76bfb1 100644 --- a/master.v +++ b/master.v @@ -1,4 +1,6 @@ `include "master_rom.v" +`include "i2c.v" +`include "uart.v" `ifdef SIM `define UART_DIVIDE 1 @@ -7,14 +9,17 @@ // s/192/3/ for 19200 baud uart `endif -module master(input CLKin, output [4:0] led, output uart_tx, input uart_rx, output reg ready_out = 1, input ready_in); - wire clk; - wire clk_tmp; +module master(input CLKin, output [4:0] led, output uart_tx, input uart_rx, output reg ready_out = 1, input ready_in, output scl, output sda); +// wire clk; +// wire clk_tmp; //pll pll (.clock_in(CLKin), .clock_out(clk)); reg [20:0] counter = 0; +`ifdef SIM + wire clk = CLKin; +`else reg clk = 0; always @ (posedge CLKin) begin @@ -25,12 +30,41 @@ module master(input CLKin, output [4:0] led, output uart_tx, input uart_rx, outp else counter <= counter + 1; end +`endif reg [3:0] program_counter = 0; wire [31:0] rom_output; master_rom master_rom (.clk(clk), .addr(program_counter), .data(rom_output)); + reg [7:0] i2c_tx_byte; + reg i2c_transmit = 0; + wire i2c_is_transmitting; + + i2c_write i2c (.clk(clk), .scl(scl), .sda(sda), .tx_byte(i2c_tx_byte), .transmit(i2c_transmit), .is_transmitting(i2c_is_transmitting)); + + reg [3:0] i2c_init_step = 0; + + always @ (posedge clk) begin + if(i2c_is_transmitting || i2c_transmit) + i2c_transmit <= 0; + else begin + if(i2c_init_step == 0) begin + i2c_tx_byte <= 8'h21; // turn on oscillator + i2c_transmit <= 1; + i2c_init_step <= 1; + end else if(i2c_init_step == 1) begin + i2c_tx_byte <= 8'h87; // display on, blink 0.5Hz + i2c_transmit <= 1; + i2c_init_step <= 2; + end else if(i2c_init_step == 2) begin + i2c_tx_byte <= 8'hEF; // max brightness + i2c_transmit <= 1; + i2c_init_step <= 3; + end + end + end + `define STATE_SEND 0 `define STATE_WAIT_PROPAGATE 1 @@ -56,7 +90,7 @@ module master(input CLKin, output [4:0] led, output uart_tx, input uart_rx, outp reg [7:0] saved_news [3:0]; assign led[4] = state != `STATE_WASTE_TIME; - assign led[3:0] = 0; + assign led[3:0] = i2c_init_step; always @(posedge clk) begin case(state) @@ -96,7 +130,7 @@ module master(input CLKin, output [4:0] led, output uart_tx, input uart_rx, outp - receive the instruction back - receive the news - propagate the news - - go back to `STATE_SEND + - go to `STATE_WASTE_TIME */ if(uart_ptr == 8) begin state <= `STATE_PROPAGATE_NEWS; -- 2.39.2