Implement a tiny part of i2c
authorMarius Gavrilescu <marius@ieval.ro>
Tue, 16 Apr 2019 15:44:02 +0000 (18:44 +0300)
committerMarius Gavrilescu <marius@ieval.ro>
Tue, 16 Apr 2019 15:44:02 +0000 (18:44 +0300)
Makefile
i2c.v [new file with mode: 0644]
master.pcf [new file with mode: 0644]
master.v

index 0fb0a575be3498fa5c8ebb7dc58637598ad34449..e2c55e51e8aeba470fb0bc42aa38029f57958b53 100644 (file)
--- 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 (file)
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 (file)
index 0000000..037381f
--- /dev/null
@@ -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
index b54abe9ad0e9ef94025b22001e66658e9082ff39..f76bfb1a9fcd11920b74597c1a02190d8c588ed7 100644 (file)
--- 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;
This page took 0.016461 seconds and 4 git commands to generate.