├── .gitignore ├── AUTHORS ├── COPYING ├── README ├── README.md ├── rtl ├── axis_fifo.v ├── i2c_init.v ├── i2c_master.v ├── i2c_master_axil.v ├── i2c_master_wbs_16.v ├── i2c_master_wbs_8.v ├── i2c_single_reg.v ├── i2c_slave.v ├── i2c_slave_axil_master.v └── i2c_slave_wbm.v └── tb ├── axil.py ├── axis_ep.py ├── i2c.py ├── test_i2c.py ├── test_i2c_init.py ├── test_i2c_init.v ├── test_i2c_master.py ├── test_i2c_master.v ├── test_i2c_master_axil.py ├── test_i2c_master_axil.v ├── test_i2c_master_wbs_16.py ├── test_i2c_master_wbs_16.v ├── test_i2c_master_wbs_8.py ├── test_i2c_master_wbs_8.v ├── test_i2c_slave.py ├── test_i2c_slave.v ├── test_i2c_slave_axil_master.py ├── test_i2c_slave_axil_master.v ├── test_i2c_slave_wbm.py ├── test_i2c_slave_wbm.v └── wb.py /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.lxt 3 | *.pyc 4 | *.vvp 5 | *.kate-swp 6 | 7 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Alex Forencich 2 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2017 Alex Forencich 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | 22 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | README.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Verilog I2C interface 2 | 3 | For more information and updates: http://alexforencich.com/wiki/en/verilog/i2c/start 4 | 5 | GitHub repository: https://github.com/alexforencich/verilog-i2c 6 | 7 | ## Deprecation Notice 8 | 9 | This repository is superseded by https://github.com/fpganinja/taxi. All new features and bug fixes will be applied there, and commercial support is also available. As a result, this repo is deprecated and will not receive any future maintenance or support. 10 | 11 | ## Introduction 12 | 13 | I2C interface components. Includes full MyHDL testbench with intelligent bus 14 | cosimulation endpoints. 15 | 16 | ## Documentation 17 | 18 | ### i2c_init module 19 | 20 | Template module for peripheral initialization via I2C. For use when one or 21 | more peripheral devices (i.e. PLL chips, jitter attenuators, clock muxes, 22 | etc.) need to be initialized on power-up without the use of a general-purpose 23 | processor. 24 | 25 | ### i2c_master module 26 | 27 | I2C master module with AXI stream interfaces to control logic. 28 | 29 | ### i2c_master_axil module 30 | 31 | I2C master module with 32-bit AXI lite slave interface. 32 | 33 | ### i2c_master_wbs_8 module 34 | 35 | I2C master module with 8-bit Wishbone slave interface. 36 | 37 | ### i2c_master_wbs_16 module 38 | 39 | I2C master module with 16-bit Wishbone slave interface. 40 | 41 | ### i2c_slave module 42 | 43 | I2C slave module with AXI stream interfaces to control logic. 44 | 45 | ### i2c_slave_axil_master module 46 | 47 | I2C slave module with parametrizable AXI lite master interface. 48 | 49 | ### i2c_slave_wbm module 50 | 51 | I2C slave module with parametrizable Wishbone master interface. 52 | 53 | 54 | ### Source Files 55 | 56 | axis_fifo.v : AXI stream FIFO 57 | i2c_init.v : Template I2C bus init state machine module 58 | i2c_master.v : I2C master module 59 | i2c_master_axil.v : I2C master module (32-bit AXI lite slave) 60 | i2c_master_wbs_8.v : I2C master module (8-bit Wishbone slave) 61 | i2c_master_wbs_16.v : I2C master module (16-bit Wishbone slave) 62 | i2c_slave.v : I2C slave module 63 | i2c_slave_axil_master.v : I2C slave module (parametrizable AXI lite master) 64 | i2c_slave_wbm.v : I2C slave module (parametrizable Wishbone master) 65 | 66 | ## Testing 67 | 68 | Running the included testbenches requires MyHDL and Icarus Verilog. Make sure 69 | that myhdl.vpi is installed properly for cosimulation to work correctly. The 70 | testbenches can be run with a Python test runner like nose or py.test, or the 71 | individual test scripts can be run with python directly. 72 | 73 | ### Testbench Files 74 | 75 | tb/axil.py : MyHDL AXI4 lite master and memory BFM 76 | tb/axis_ep.py : MyHDL AXI Stream endpoints 77 | tb/i2c.py : MyHDL I2C master and slave models 78 | tb/wb.py : MyHDL Wishbone master model and RAM model 79 | -------------------------------------------------------------------------------- /rtl/axis_fifo.v: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2013-2018 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | // Language: Verilog 2001 26 | 27 | `timescale 1ns / 1ps 28 | 29 | /* 30 | * AXI4-Stream FIFO 31 | */ 32 | module axis_fifo # 33 | ( 34 | // FIFO depth in words 35 | // KEEP_WIDTH words per cycle if KEEP_ENABLE set 36 | // Rounded up to nearest power of 2 cycles 37 | parameter DEPTH = 4096, 38 | // Width of AXI stream interfaces in bits 39 | parameter DATA_WIDTH = 8, 40 | // Propagate tkeep signal 41 | // If disabled, tkeep assumed to be 1'b1 42 | parameter KEEP_ENABLE = (DATA_WIDTH>8), 43 | // tkeep signal width (words per cycle) 44 | parameter KEEP_WIDTH = (DATA_WIDTH/8), 45 | // Propagate tlast signal 46 | parameter LAST_ENABLE = 1, 47 | // Propagate tid signal 48 | parameter ID_ENABLE = 0, 49 | // tid signal width 50 | parameter ID_WIDTH = 8, 51 | // Propagate tdest signal 52 | parameter DEST_ENABLE = 0, 53 | // tdest signal width 54 | parameter DEST_WIDTH = 8, 55 | // Propagate tuser signal 56 | parameter USER_ENABLE = 1, 57 | // tuser signal width 58 | parameter USER_WIDTH = 1, 59 | // number of output pipeline registers 60 | parameter PIPELINE_OUTPUT = 2, 61 | // Frame FIFO mode - operate on frames instead of cycles 62 | // When set, m_axis_tvalid will not be deasserted within a frame 63 | // Requires LAST_ENABLE set 64 | parameter FRAME_FIFO = 0, 65 | // tuser value for bad frame marker 66 | parameter USER_BAD_FRAME_VALUE = 1'b1, 67 | // tuser mask for bad frame marker 68 | parameter USER_BAD_FRAME_MASK = 1'b1, 69 | // Drop frames marked bad 70 | // Requires FRAME_FIFO set 71 | parameter DROP_BAD_FRAME = 0, 72 | // Drop incoming frames when full 73 | // When set, s_axis_tready is always asserted 74 | // Requires FRAME_FIFO set 75 | parameter DROP_WHEN_FULL = 0 76 | ) 77 | ( 78 | input wire clk, 79 | input wire rst, 80 | 81 | /* 82 | * AXI input 83 | */ 84 | input wire [DATA_WIDTH-1:0] s_axis_tdata, 85 | input wire [KEEP_WIDTH-1:0] s_axis_tkeep, 86 | input wire s_axis_tvalid, 87 | output wire s_axis_tready, 88 | input wire s_axis_tlast, 89 | input wire [ID_WIDTH-1:0] s_axis_tid, 90 | input wire [DEST_WIDTH-1:0] s_axis_tdest, 91 | input wire [USER_WIDTH-1:0] s_axis_tuser, 92 | 93 | /* 94 | * AXI output 95 | */ 96 | output wire [DATA_WIDTH-1:0] m_axis_tdata, 97 | output wire [KEEP_WIDTH-1:0] m_axis_tkeep, 98 | output wire m_axis_tvalid, 99 | input wire m_axis_tready, 100 | output wire m_axis_tlast, 101 | output wire [ID_WIDTH-1:0] m_axis_tid, 102 | output wire [DEST_WIDTH-1:0] m_axis_tdest, 103 | output wire [USER_WIDTH-1:0] m_axis_tuser, 104 | 105 | /* 106 | * Status 107 | */ 108 | output wire status_overflow, 109 | output wire status_bad_frame, 110 | output wire status_good_frame, 111 | output wire status_full, 112 | output wire status_empty 113 | ); 114 | 115 | parameter ADDR_WIDTH = (KEEP_ENABLE && KEEP_WIDTH > 1) ? $clog2(DEPTH/KEEP_WIDTH) : $clog2(DEPTH); 116 | 117 | // check configuration 118 | initial begin 119 | if (PIPELINE_OUTPUT < 1) begin 120 | $error("Error: PIPELINE_OUTPUT must be at least 1 (instance %m)"); 121 | $finish; 122 | end 123 | 124 | if (FRAME_FIFO && !LAST_ENABLE) begin 125 | $error("Error: FRAME_FIFO set requires LAST_ENABLE set (instance %m)"); 126 | $finish; 127 | end 128 | 129 | if (DROP_BAD_FRAME && !FRAME_FIFO) begin 130 | $error("Error: DROP_BAD_FRAME set requires FRAME_FIFO set (instance %m)"); 131 | $finish; 132 | end 133 | 134 | if (DROP_WHEN_FULL && !FRAME_FIFO) begin 135 | $error("Error: DROP_WHEN_FULL set requires FRAME_FIFO set (instance %m)"); 136 | $finish; 137 | end 138 | 139 | if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin 140 | $error("Error: Invalid USER_BAD_FRAME_MASK value (instance %m)"); 141 | $finish; 142 | end 143 | end 144 | 145 | localparam KEEP_OFFSET = DATA_WIDTH; 146 | localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0); 147 | localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0); 148 | localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0); 149 | localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0); 150 | localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0); 151 | 152 | reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}; 153 | reg [ADDR_WIDTH:0] wr_ptr_cur_reg = {ADDR_WIDTH+1{1'b0}}; 154 | reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}; 155 | 156 | reg [WIDTH-1:0] mem[(2**ADDR_WIDTH)-1:0]; 157 | reg [WIDTH-1:0] mem_read_data_reg; 158 | reg mem_read_data_valid_reg = 1'b0; 159 | 160 | wire [WIDTH-1:0] s_axis; 161 | 162 | reg [WIDTH-1:0] m_axis_pipe_reg[PIPELINE_OUTPUT-1:0]; 163 | reg [PIPELINE_OUTPUT-1:0] m_axis_tvalid_pipe_reg = 1'b0; 164 | 165 | // full when first MSB different but rest same 166 | wire full = wr_ptr_reg == (rd_ptr_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}}); 167 | wire full_cur = wr_ptr_cur_reg == (rd_ptr_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}}); 168 | // empty when pointers match exactly 169 | wire empty = wr_ptr_reg == rd_ptr_reg; 170 | // overflow within packet 171 | wire full_wr = wr_ptr_reg == (wr_ptr_cur_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}}); 172 | 173 | reg drop_frame_reg = 1'b0; 174 | reg overflow_reg = 1'b0; 175 | reg bad_frame_reg = 1'b0; 176 | reg good_frame_reg = 1'b0; 177 | 178 | assign s_axis_tready = FRAME_FIFO ? (!full_cur || full_wr || DROP_WHEN_FULL) : !full; 179 | 180 | generate 181 | assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata; 182 | if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep; 183 | if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast; 184 | if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid; 185 | if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest; 186 | if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser; 187 | endgenerate 188 | 189 | assign m_axis_tvalid = m_axis_tvalid_pipe_reg[PIPELINE_OUTPUT-1]; 190 | 191 | assign m_axis_tdata = m_axis_pipe_reg[PIPELINE_OUTPUT-1][DATA_WIDTH-1:0]; 192 | assign m_axis_tkeep = KEEP_ENABLE ? m_axis_pipe_reg[PIPELINE_OUTPUT-1][KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}}; 193 | assign m_axis_tlast = LAST_ENABLE ? m_axis_pipe_reg[PIPELINE_OUTPUT-1][LAST_OFFSET] : 1'b1; 194 | assign m_axis_tid = ID_ENABLE ? m_axis_pipe_reg[PIPELINE_OUTPUT-1][ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}}; 195 | assign m_axis_tdest = DEST_ENABLE ? m_axis_pipe_reg[PIPELINE_OUTPUT-1][DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}}; 196 | assign m_axis_tuser = USER_ENABLE ? m_axis_pipe_reg[PIPELINE_OUTPUT-1][USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}}; 197 | 198 | assign status_overflow = overflow_reg; 199 | assign status_bad_frame = bad_frame_reg; 200 | assign status_good_frame = good_frame_reg; 201 | assign status_full = FRAME_FIFO ? full_cur || full_wr : full; 202 | assign status_empty = empty; 203 | 204 | // Write logic 205 | always @(posedge clk) begin 206 | overflow_reg <= 1'b0; 207 | bad_frame_reg <= 1'b0; 208 | good_frame_reg <= 1'b0; 209 | 210 | if (s_axis_tready && s_axis_tvalid) begin 211 | // transfer in 212 | if (!FRAME_FIFO) begin 213 | // normal FIFO mode 214 | mem[wr_ptr_reg[ADDR_WIDTH-1:0]] <= s_axis; 215 | wr_ptr_reg <= wr_ptr_reg + 1; 216 | end else if (full_cur || full_wr || drop_frame_reg) begin 217 | // full, packet overflow, or currently dropping frame 218 | // drop frame 219 | drop_frame_reg <= 1'b1; 220 | if (s_axis_tlast) begin 221 | // end of frame, reset write pointer 222 | wr_ptr_cur_reg <= wr_ptr_reg; 223 | drop_frame_reg <= 1'b0; 224 | overflow_reg <= 1'b1; 225 | end 226 | end else begin 227 | mem[wr_ptr_cur_reg[ADDR_WIDTH-1:0]] <= s_axis; 228 | wr_ptr_cur_reg <= wr_ptr_cur_reg + 1; 229 | if (s_axis_tlast) begin 230 | // end of frame 231 | if (DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(s_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin 232 | // bad packet, reset write pointer 233 | wr_ptr_cur_reg <= wr_ptr_reg; 234 | bad_frame_reg <= 1'b1; 235 | end else begin 236 | // good packet, update write pointer 237 | wr_ptr_reg <= wr_ptr_cur_reg + 1; 238 | good_frame_reg <= 1'b1; 239 | end 240 | end 241 | end 242 | end 243 | 244 | if (rst) begin 245 | wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; 246 | wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}}; 247 | 248 | drop_frame_reg <= 1'b0; 249 | overflow_reg <= 1'b0; 250 | bad_frame_reg <= 1'b0; 251 | good_frame_reg <= 1'b0; 252 | end 253 | end 254 | 255 | // Read logic 256 | integer j; 257 | 258 | always @(posedge clk) begin 259 | if (m_axis_tready) begin 260 | // output ready; invalidate stage 261 | m_axis_tvalid_pipe_reg[PIPELINE_OUTPUT-1] <= 1'b0; 262 | end 263 | 264 | for (j = PIPELINE_OUTPUT-1; j > 0; j = j - 1) begin 265 | if (m_axis_tready || ((~m_axis_tvalid_pipe_reg) >> j)) begin 266 | // output ready or bubble in pipeline; transfer down pipeline 267 | m_axis_tvalid_pipe_reg[j] <= m_axis_tvalid_pipe_reg[j-1]; 268 | m_axis_pipe_reg[j] <= m_axis_pipe_reg[j-1]; 269 | m_axis_tvalid_pipe_reg[j-1] <= 1'b0; 270 | end 271 | end 272 | 273 | if (m_axis_tready || ~m_axis_tvalid_pipe_reg) begin 274 | // output ready or bubble in pipeline; read new data from FIFO 275 | m_axis_tvalid_pipe_reg[0] <= 1'b0; 276 | m_axis_pipe_reg[0] <= mem[rd_ptr_reg[ADDR_WIDTH-1:0]]; 277 | if (!empty) begin 278 | // not empty, increment pointer 279 | m_axis_tvalid_pipe_reg[0] <= 1'b1; 280 | rd_ptr_reg <= rd_ptr_reg + 1; 281 | end 282 | end 283 | 284 | if (rst) begin 285 | rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; 286 | m_axis_tvalid_pipe_reg <= {PIPELINE_OUTPUT{1'b0}}; 287 | end 288 | end 289 | 290 | endmodule 291 | -------------------------------------------------------------------------------- /rtl/i2c_single_reg.v: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2023 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | // Language: Verilog 2001 26 | 27 | `resetall 28 | `timescale 1ns / 1ps 29 | `default_nettype none 30 | 31 | /* 32 | * I2C single register 33 | */ 34 | module i2c_single_reg #( 35 | parameter FILTER_LEN = 4, 36 | parameter DEV_ADDR = 7'h70 37 | ) 38 | ( 39 | input wire clk, 40 | input wire rst, 41 | 42 | /* 43 | * I2C interface 44 | */ 45 | input wire scl_i, 46 | output wire scl_o, 47 | output wire scl_t, 48 | input wire sda_i, 49 | output wire sda_o, 50 | output wire sda_t, 51 | 52 | /* 53 | * Data register 54 | */ 55 | input wire [7:0] data_in, 56 | input wire data_latch, 57 | output wire [7:0] data_out 58 | ); 59 | 60 | localparam [4:0] 61 | STATE_IDLE = 4'd0, 62 | STATE_ADDRESS = 4'd1, 63 | STATE_ACK = 4'd2, 64 | STATE_WRITE_1 = 4'd3, 65 | STATE_WRITE_2 = 4'd4, 66 | STATE_READ_1 = 4'd5, 67 | STATE_READ_2 = 4'd6, 68 | STATE_READ_3 = 4'd7; 69 | 70 | reg [4:0] state_reg = STATE_IDLE; 71 | 72 | reg [7:0] data_reg = 8'd0; 73 | reg [7:0] shift_reg = 8'd0; 74 | 75 | reg mode_read_reg = 1'b0; 76 | 77 | reg [3:0] bit_count_reg = 4'd0; 78 | 79 | reg [FILTER_LEN-1:0] scl_i_filter_reg = {FILTER_LEN{1'b1}}; 80 | reg [FILTER_LEN-1:0] sda_i_filter_reg = {FILTER_LEN{1'b1}}; 81 | 82 | reg scl_i_reg = 1'b1; 83 | reg sda_i_reg = 1'b1; 84 | 85 | reg sda_o_reg = 1'b1; 86 | 87 | reg last_scl_i_reg = 1'b1; 88 | reg last_sda_i_reg = 1'b1; 89 | 90 | assign scl_o = 1'b1; 91 | assign scl_t = 1'b1; 92 | assign sda_o = sda_o_reg; 93 | assign sda_t = sda_o_reg; 94 | 95 | assign data_out = data_reg; 96 | 97 | wire scl_posedge = scl_i_reg && !last_scl_i_reg; 98 | wire scl_negedge = !scl_i_reg && last_scl_i_reg; 99 | wire sda_posedge = sda_i_reg && !last_sda_i_reg; 100 | wire sda_negedge = !sda_i_reg && last_sda_i_reg; 101 | 102 | wire start_bit = sda_negedge && scl_i_reg; 103 | wire stop_bit = sda_posedge && scl_i_reg; 104 | 105 | always @(posedge clk) begin 106 | 107 | if (start_bit) begin 108 | sda_o_reg <= 1'b1; 109 | 110 | bit_count_reg = 4'd7; 111 | state_reg <= STATE_ADDRESS; 112 | end else if (stop_bit) begin 113 | sda_o_reg <= 1'b1; 114 | 115 | state_reg <= STATE_IDLE; 116 | end else begin 117 | case (state_reg) 118 | STATE_IDLE: begin 119 | // line idle 120 | sda_o_reg <= 1'b1; 121 | 122 | state_reg <= STATE_IDLE; 123 | end 124 | STATE_ADDRESS: begin 125 | // read address 126 | sda_o_reg <= 1'b1; 127 | 128 | if (scl_posedge) begin 129 | if (bit_count_reg > 0) begin 130 | // shift in address 131 | bit_count_reg <= bit_count_reg-1; 132 | shift_reg <= {shift_reg[6:0], sda_i_reg}; 133 | state_reg <= STATE_ADDRESS; 134 | end else begin 135 | // check address 136 | mode_read_reg <= sda_i_reg; 137 | if (shift_reg[6:0] == DEV_ADDR) begin 138 | // it's a match, send ACK 139 | state_reg <= STATE_ACK; 140 | end else begin 141 | // no match, return to idle 142 | state_reg <= STATE_IDLE; 143 | end 144 | end 145 | end else begin 146 | state_reg <= STATE_ADDRESS; 147 | end 148 | end 149 | STATE_ACK: begin 150 | // send ACK bit 151 | if (scl_negedge) begin 152 | sda_o_reg <= 1'b0; 153 | bit_count_reg <= 4'd7; 154 | if (mode_read_reg) begin 155 | // reading 156 | shift_reg <= data_reg; 157 | state_reg <= STATE_READ_1; 158 | end else begin 159 | // writing 160 | state_reg <= STATE_WRITE_1; 161 | end 162 | end else begin 163 | state_reg <= STATE_ACK; 164 | end 165 | end 166 | STATE_WRITE_1: begin 167 | // write data byte 168 | if (scl_negedge) begin 169 | sda_o_reg <= 1'b1; 170 | state_reg <= STATE_WRITE_2; 171 | end else begin 172 | state_reg <= STATE_WRITE_1; 173 | end 174 | end 175 | STATE_WRITE_2: begin 176 | // write data byte 177 | sda_o_reg <= 1'b1; 178 | if (scl_posedge) begin 179 | // shift in data bit 180 | shift_reg <= {shift_reg[6:0], sda_i_reg}; 181 | if (bit_count_reg > 0) begin 182 | bit_count_reg <= bit_count_reg-1; 183 | state_reg <= STATE_WRITE_2; 184 | end else begin 185 | data_reg <= {shift_reg[6:0], sda_i_reg}; 186 | state_reg <= STATE_ACK; 187 | end 188 | end else begin 189 | state_reg <= STATE_WRITE_2; 190 | end 191 | end 192 | STATE_READ_1: begin 193 | // read data byte 194 | if (scl_negedge) begin 195 | // shift out data bit 196 | {sda_o_reg, shift_reg} = {shift_reg, sda_i_reg}; 197 | 198 | if (bit_count_reg > 0) begin 199 | bit_count_reg = bit_count_reg-1; 200 | state_reg = STATE_READ_1; 201 | end else begin 202 | state_reg = STATE_READ_2; 203 | end 204 | end else begin 205 | state_reg = STATE_READ_1; 206 | end 207 | end 208 | STATE_READ_2: begin 209 | // read ACK bit 210 | if (scl_negedge) begin 211 | // release SDA 212 | sda_o_reg <= 1'b1; 213 | state_reg <= STATE_READ_3; 214 | end else begin 215 | state_reg <= STATE_READ_2; 216 | end 217 | end 218 | STATE_READ_3: begin 219 | // read ACK bit 220 | if (scl_posedge) begin 221 | if (sda_i_reg) begin 222 | // NACK, return to idle 223 | state_reg <= STATE_IDLE; 224 | end else begin 225 | // ACK, read another byte 226 | bit_count_reg <= 4'd7; 227 | shift_reg <= data_reg; 228 | state_reg <= STATE_READ_1; 229 | end 230 | end else begin 231 | state_reg <= STATE_READ_3; 232 | end 233 | end 234 | endcase 235 | end 236 | 237 | if (data_latch) begin 238 | data_reg <= data_in; 239 | end 240 | 241 | scl_i_filter_reg <= (scl_i_filter_reg << 1) | scl_i; 242 | sda_i_filter_reg <= (sda_i_filter_reg << 1) | sda_i; 243 | 244 | if (scl_i_filter_reg == {FILTER_LEN{1'b1}}) begin 245 | scl_i_reg <= 1'b1; 246 | end else if (scl_i_filter_reg == {FILTER_LEN{1'b0}}) begin 247 | scl_i_reg <= 1'b0; 248 | end 249 | 250 | if (sda_i_filter_reg == {FILTER_LEN{1'b1}}) begin 251 | sda_i_reg <= 1'b1; 252 | end else if (sda_i_filter_reg == {FILTER_LEN{1'b0}}) begin 253 | sda_i_reg <= 1'b0; 254 | end 255 | 256 | last_scl_i_reg <= scl_i_reg; 257 | last_sda_i_reg <= sda_i_reg; 258 | 259 | if (rst) begin 260 | state_reg <= STATE_IDLE; 261 | sda_o_reg <= 1'b1; 262 | end 263 | end 264 | 265 | endmodule 266 | 267 | `resetall 268 | -------------------------------------------------------------------------------- /rtl/i2c_slave.v: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2017 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | // Language: Verilog 2001 26 | 27 | `timescale 1ns / 1ps 28 | 29 | /* 30 | * I2C slave 31 | */ 32 | module i2c_slave #( 33 | parameter FILTER_LEN = 4 34 | ) 35 | ( 36 | input wire clk, 37 | input wire rst, 38 | 39 | /* 40 | * Host interface 41 | */ 42 | input wire release_bus, 43 | 44 | input wire [7:0] s_axis_data_tdata, 45 | input wire s_axis_data_tvalid, 46 | output wire s_axis_data_tready, 47 | input wire s_axis_data_tlast, 48 | 49 | output wire [7:0] m_axis_data_tdata, 50 | output wire m_axis_data_tvalid, 51 | input wire m_axis_data_tready, 52 | output wire m_axis_data_tlast, 53 | 54 | /* 55 | * I2C interface 56 | */ 57 | input wire scl_i, 58 | output wire scl_o, 59 | output wire scl_t, 60 | input wire sda_i, 61 | output wire sda_o, 62 | output wire sda_t, 63 | 64 | /* 65 | * Status 66 | */ 67 | output wire busy, 68 | output wire [6:0] bus_address, 69 | output wire bus_addressed, 70 | output wire bus_active, 71 | 72 | /* 73 | * Configuration 74 | */ 75 | input wire enable, 76 | input wire [6:0] device_address, 77 | input wire [6:0] device_address_mask 78 | ); 79 | /* 80 | 81 | I2C 82 | 83 | Read 84 | __ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __ 85 | sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_\_R___A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A____/ 86 | ____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____ 87 | scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP 88 | 89 | Write 90 | __ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __ 91 | sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_/ W \_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_/ N \__/ 92 | ____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____ 93 | scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP 94 | 95 | 96 | Operation: 97 | 98 | This module translates I2C read and write operations into AXI stream transfers. 99 | Bytes written over I2C will be delayed by one byte time so that the last byte 100 | in a write operation can be accurately marked. When reading, the module will 101 | stretch SCL by holding it low until a data byte is presented at the AXI stream 102 | input. 103 | 104 | Control: 105 | 106 | release_bus 107 | releases control over bus 108 | 109 | Status: 110 | 111 | busy 112 | module is communicating over the bus 113 | 114 | bus_address 115 | active address on bus when module is addressed 116 | 117 | bus_addressed 118 | module is currently addressed on the bus 119 | 120 | bus_active 121 | bus is active, not necessarily controlled by this module 122 | 123 | Parameters: 124 | 125 | device_address 126 | address of slave device 127 | 128 | device_address_mask 129 | select which bits of device address to compare, set to 7'h7f 130 | to check all bits (single address device) 131 | 132 | Example of interfacing with tristate pins: 133 | (this will work for any tristate bus) 134 | 135 | assign scl_i = scl_pin; 136 | assign scl_pin = scl_t ? 1'bz : scl_o; 137 | assign sda_i = sda_pin; 138 | assign sda_pin = sda_t ? 1'bz : sda_o; 139 | 140 | Equivalent code that does not use *_t connections: 141 | (we can get away with this because I2C is open-drain) 142 | 143 | assign scl_i = scl_pin; 144 | assign scl_pin = scl_o ? 1'bz : 1'b0; 145 | assign sda_i = sda_pin; 146 | assign sda_pin = sda_o ? 1'bz : 1'b0; 147 | 148 | Example of two interconnected I2C devices: 149 | 150 | assign scl_1_i = scl_1_o & scl_2_o; 151 | assign scl_2_i = scl_1_o & scl_2_o; 152 | assign sda_1_i = sda_1_o & sda_2_o; 153 | assign sda_2_i = sda_1_o & sda_2_o; 154 | 155 | Example of two I2C devices sharing the same pins: 156 | 157 | assign scl_1_i = scl_pin; 158 | assign scl_2_i = scl_pin; 159 | assign scl_pin = (scl_1_o & scl_2_o) ? 1'bz : 1'b0; 160 | assign sda_1_i = sda_pin; 161 | assign sda_2_i = sda_pin; 162 | assign sda_pin = (sda_1_o & sda_2_o) ? 1'bz : 1'b0; 163 | 164 | Notes: 165 | 166 | scl_o should not be connected directly to scl_i, only via AND logic or a tristate 167 | I/O pin. This would prevent devices from stretching the clock period. 168 | 169 | */ 170 | 171 | localparam [4:0] 172 | STATE_IDLE = 4'd0, 173 | STATE_ADDRESS = 4'd1, 174 | STATE_ACK = 4'd2, 175 | STATE_WRITE_1 = 4'd3, 176 | STATE_WRITE_2 = 4'd4, 177 | STATE_READ_1 = 4'd5, 178 | STATE_READ_2 = 4'd6, 179 | STATE_READ_3 = 4'd7; 180 | 181 | reg [4:0] state_reg = STATE_IDLE, state_next; 182 | 183 | reg [6:0] addr_reg = 7'd0, addr_next; 184 | reg [7:0] data_reg = 8'd0, data_next; 185 | reg data_valid_reg = 1'b0, data_valid_next; 186 | reg data_out_reg_valid_reg = 1'b0, data_out_reg_valid_next; 187 | reg last_reg = 1'b0, last_next; 188 | 189 | reg mode_read_reg = 1'b0, mode_read_next; 190 | 191 | reg [3:0] bit_count_reg = 4'd0, bit_count_next; 192 | 193 | reg s_axis_data_tready_reg = 1'b0, s_axis_data_tready_next; 194 | 195 | reg [7:0] m_axis_data_tdata_reg = 8'd0, m_axis_data_tdata_next; 196 | reg m_axis_data_tvalid_reg = 1'b0, m_axis_data_tvalid_next; 197 | reg m_axis_data_tlast_reg = 1'b0, m_axis_data_tlast_next; 198 | 199 | reg [FILTER_LEN-1:0] scl_i_filter = {FILTER_LEN{1'b1}}; 200 | reg [FILTER_LEN-1:0] sda_i_filter = {FILTER_LEN{1'b1}}; 201 | 202 | reg scl_i_reg = 1'b1; 203 | reg sda_i_reg = 1'b1; 204 | 205 | reg scl_o_reg = 1'b1, scl_o_next; 206 | reg sda_o_reg = 1'b1, sda_o_next; 207 | 208 | reg last_scl_i_reg = 1'b1; 209 | reg last_sda_i_reg = 1'b1; 210 | 211 | reg busy_reg = 1'b0; 212 | reg bus_active_reg = 1'b0; 213 | reg bus_addressed_reg = 1'b0, bus_addressed_next; 214 | 215 | assign bus_address = addr_reg; 216 | 217 | assign s_axis_data_tready = s_axis_data_tready_reg; 218 | 219 | assign m_axis_data_tdata = m_axis_data_tdata_reg; 220 | assign m_axis_data_tvalid = m_axis_data_tvalid_reg; 221 | assign m_axis_data_tlast = m_axis_data_tlast_reg; 222 | 223 | assign scl_o = scl_o_reg; 224 | assign scl_t = scl_o_reg; 225 | assign sda_o = sda_o_reg; 226 | assign sda_t = sda_o_reg; 227 | 228 | assign busy = busy_reg; 229 | assign bus_active = bus_active_reg; 230 | assign bus_addressed = bus_addressed_reg; 231 | 232 | assign scl_posedge = scl_i_reg && !last_scl_i_reg; 233 | assign scl_negedge = !scl_i_reg && last_scl_i_reg; 234 | assign sda_posedge = sda_i_reg && !last_sda_i_reg; 235 | assign sda_negedge = !sda_i_reg && last_sda_i_reg; 236 | 237 | assign start_bit = sda_negedge && scl_i_reg; 238 | assign stop_bit = sda_posedge && scl_i_reg; 239 | 240 | always @* begin 241 | state_next = STATE_IDLE; 242 | 243 | addr_next = addr_reg; 244 | data_next = data_reg; 245 | data_valid_next = data_valid_reg; 246 | data_out_reg_valid_next = data_out_reg_valid_reg; 247 | last_next = last_reg; 248 | 249 | mode_read_next = mode_read_reg; 250 | 251 | bit_count_next = bit_count_reg; 252 | 253 | s_axis_data_tready_next = 1'b0; 254 | 255 | m_axis_data_tdata_next = m_axis_data_tdata_reg; 256 | m_axis_data_tvalid_next = m_axis_data_tvalid_reg && !m_axis_data_tready; 257 | m_axis_data_tlast_next = m_axis_data_tlast_reg; 258 | 259 | scl_o_next = scl_o_reg; 260 | sda_o_next = sda_o_reg; 261 | 262 | bus_addressed_next = bus_addressed_reg; 263 | 264 | if (start_bit) begin 265 | // got start bit, latch out data, read address 266 | data_valid_next = 1'b0; 267 | data_out_reg_valid_next = 1'b0; 268 | bit_count_next = 4'd7; 269 | m_axis_data_tlast_next = 1'b1; 270 | m_axis_data_tvalid_next = data_out_reg_valid_reg; 271 | bus_addressed_next = 1'b0; 272 | state_next = STATE_ADDRESS; 273 | end else if (release_bus || stop_bit) begin 274 | // got stop bit or release bus command, latch out data, return to idle 275 | data_valid_next = 1'b0; 276 | data_out_reg_valid_next = 1'b0; 277 | m_axis_data_tlast_next = 1'b1; 278 | m_axis_data_tvalid_next = data_out_reg_valid_reg; 279 | bus_addressed_next = 1'b0; 280 | state_next = STATE_IDLE; 281 | end else begin 282 | case (state_reg) 283 | STATE_IDLE: begin 284 | // line idle 285 | data_valid_next = 1'b0; 286 | data_out_reg_valid_next = 1'b0; 287 | bus_addressed_next = 1'b0; 288 | state_next = STATE_IDLE; 289 | end 290 | STATE_ADDRESS: begin 291 | // read address 292 | if (scl_posedge) begin 293 | if (bit_count_reg > 0) begin 294 | // shift in address 295 | bit_count_next = bit_count_reg-1; 296 | data_next = {data_reg[6:0], sda_i_reg}; 297 | state_next = STATE_ADDRESS; 298 | end else begin 299 | // check address 300 | if (enable && (device_address & device_address_mask) == (data_reg[6:0] & device_address_mask)) begin 301 | // it's a match, save read/write bit and send ACK 302 | addr_next = data_reg[6:0]; 303 | mode_read_next = sda_i_reg; 304 | bus_addressed_next = 1'b1; 305 | state_next = STATE_ACK; 306 | end else begin 307 | // no match, return to idle 308 | state_next = STATE_IDLE; 309 | end 310 | end 311 | end else begin 312 | state_next = STATE_ADDRESS; 313 | end 314 | end 315 | STATE_ACK: begin 316 | // send ACK bit 317 | if (scl_negedge) begin 318 | sda_o_next = 1'b0; 319 | bit_count_next = 4'd7; 320 | if (mode_read_reg) begin 321 | // reading 322 | s_axis_data_tready_next = 1'b1; 323 | data_valid_next = 1'b0; 324 | state_next = STATE_READ_1; 325 | end else begin 326 | // writing 327 | state_next = STATE_WRITE_1; 328 | end 329 | end else begin 330 | state_next = STATE_ACK; 331 | end 332 | end 333 | STATE_WRITE_1: begin 334 | // write data byte 335 | if (scl_negedge || !scl_o_reg) begin 336 | sda_o_next = 1'b1; 337 | if (m_axis_data_tvalid && !m_axis_data_tready) begin 338 | // data waiting in output register, so stretch clock 339 | scl_o_next = 1'b0; 340 | state_next = STATE_WRITE_1; 341 | end else begin 342 | scl_o_next = 1'b1; 343 | if (data_valid_reg) begin 344 | // store data in output register 345 | m_axis_data_tdata_next = data_reg; 346 | m_axis_data_tlast_next = 1'b0; 347 | end 348 | data_valid_next = 1'b0; 349 | data_out_reg_valid_next = data_valid_reg; 350 | state_next = STATE_WRITE_2; 351 | end 352 | end else begin 353 | state_next = STATE_WRITE_1; 354 | end 355 | end 356 | STATE_WRITE_2: begin 357 | // write data byte 358 | if (scl_posedge) begin 359 | // shift in data bit 360 | data_next = {data_reg[6:0], sda_i_reg}; 361 | if (bit_count_reg > 0) begin 362 | bit_count_next = bit_count_reg-1; 363 | state_next = STATE_WRITE_2; 364 | end else begin 365 | // latch out previous data byte since we now know it's not the last one 366 | m_axis_data_tvalid_next = data_out_reg_valid_reg; 367 | data_out_reg_valid_next = 1'b0; 368 | data_valid_next = 1'b1; 369 | state_next = STATE_ACK; 370 | end 371 | end else begin 372 | state_next = STATE_WRITE_2; 373 | end 374 | end 375 | STATE_READ_1: begin 376 | // read data byte 377 | if (s_axis_data_tready && s_axis_data_tvalid) begin 378 | // data valid; latch it in 379 | s_axis_data_tready_next = 1'b0; 380 | data_next = s_axis_data_tdata; 381 | data_valid_next = 1'b1; 382 | end else begin 383 | // keep ready high if we're waiting for data 384 | s_axis_data_tready_next = !data_valid_reg; 385 | end 386 | 387 | if (scl_negedge || !scl_o_reg) begin 388 | // shift out data bit 389 | if (!data_valid_reg) begin 390 | // waiting for data, so stretch clock 391 | scl_o_next = 1'b0; 392 | state_next = STATE_READ_1; 393 | end else begin 394 | scl_o_next = 1'b1; 395 | {sda_o_next, data_next} = {data_reg, 1'b0}; 396 | 397 | if (bit_count_reg > 0) begin 398 | bit_count_next = bit_count_reg-1; 399 | state_next = STATE_READ_1; 400 | end else begin 401 | state_next = STATE_READ_2; 402 | end 403 | end 404 | end else begin 405 | state_next = STATE_READ_1; 406 | end 407 | end 408 | STATE_READ_2: begin 409 | // read ACK bit 410 | if (scl_negedge) begin 411 | // release SDA 412 | sda_o_next = 1'b1; 413 | state_next = STATE_READ_3; 414 | end else begin 415 | state_next = STATE_READ_2; 416 | end 417 | end 418 | STATE_READ_3: begin 419 | // read ACK bit 420 | if (scl_posedge) begin 421 | if (sda_i_reg) begin 422 | // NACK, return to idle 423 | state_next = STATE_IDLE; 424 | end else begin 425 | // ACK, read another byte 426 | bit_count_next = 4'd7; 427 | s_axis_data_tready_next = 1'b1; 428 | data_valid_next = 1'b0; 429 | state_next = STATE_READ_1; 430 | end 431 | end else begin 432 | state_next = STATE_READ_3; 433 | end 434 | end 435 | endcase 436 | end 437 | end 438 | 439 | always @(posedge clk) begin 440 | state_reg <= state_next; 441 | 442 | addr_reg <= addr_next; 443 | data_reg <= data_next; 444 | data_valid_reg <= data_valid_next; 445 | data_out_reg_valid_reg <= data_out_reg_valid_next; 446 | last_reg <= last_next; 447 | 448 | mode_read_reg <= mode_read_next; 449 | 450 | bit_count_reg <= bit_count_next; 451 | 452 | s_axis_data_tready_reg <= s_axis_data_tready_next; 453 | 454 | m_axis_data_tdata_reg <= m_axis_data_tdata_next; 455 | m_axis_data_tvalid_reg <= m_axis_data_tvalid_next; 456 | m_axis_data_tlast_reg <= m_axis_data_tlast_next; 457 | 458 | scl_i_filter <= (scl_i_filter << 1) | scl_i; 459 | sda_i_filter <= (sda_i_filter << 1) | sda_i; 460 | 461 | if (scl_i_filter == {FILTER_LEN{1'b1}}) begin 462 | scl_i_reg <= 1'b1; 463 | end else if (scl_i_filter == {FILTER_LEN{1'b0}}) begin 464 | scl_i_reg <= 1'b0; 465 | end 466 | 467 | if (sda_i_filter == {FILTER_LEN{1'b1}}) begin 468 | sda_i_reg <= 1'b1; 469 | end else if (sda_i_filter == {FILTER_LEN{1'b0}}) begin 470 | sda_i_reg <= 1'b0; 471 | end 472 | 473 | scl_o_reg <= scl_o_next; 474 | sda_o_reg <= sda_o_next; 475 | 476 | last_scl_i_reg <= scl_i_reg; 477 | last_sda_i_reg <= sda_i_reg; 478 | 479 | busy_reg <= !(state_reg == STATE_IDLE); 480 | 481 | if (start_bit) begin 482 | bus_active_reg <= 1'b1; 483 | end else if (stop_bit) begin 484 | bus_active_reg <= 1'b0; 485 | end else begin 486 | bus_active_reg <= bus_active_reg; 487 | end 488 | 489 | bus_addressed_reg <= bus_addressed_next; 490 | 491 | if (rst) begin 492 | state_reg <= STATE_IDLE; 493 | s_axis_data_tready_reg <= 1'b0; 494 | m_axis_data_tvalid_reg <= 1'b0; 495 | scl_o_reg <= 1'b1; 496 | sda_o_reg <= 1'b1; 497 | busy_reg <= 1'b0; 498 | bus_active_reg <= 1'b0; 499 | bus_addressed_reg <= 1'b0; 500 | end 501 | end 502 | 503 | endmodule 504 | -------------------------------------------------------------------------------- /rtl/i2c_slave_wbm.v: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2017 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | // Language: Verilog 2001 26 | 27 | `timescale 1ns / 1ps 28 | 29 | /* 30 | * I2C slave wishbone master wrapper 31 | */ 32 | module i2c_slave_wbm # 33 | ( 34 | parameter FILTER_LEN = 4, 35 | parameter WB_DATA_WIDTH = 32, // width of data bus in bits (8, 16, 32, or 64) 36 | parameter WB_ADDR_WIDTH = 32, // width of address bus in bits 37 | parameter WB_SELECT_WIDTH = (WB_DATA_WIDTH/8) // width of word select bus (1, 2, 4, or 8) 38 | ) 39 | ( 40 | input wire clk, 41 | input wire rst, 42 | 43 | /* 44 | * I2C interface 45 | */ 46 | input wire i2c_scl_i, 47 | output wire i2c_scl_o, 48 | output wire i2c_scl_t, 49 | input wire i2c_sda_i, 50 | output wire i2c_sda_o, 51 | output wire i2c_sda_t, 52 | 53 | /* 54 | * Wishbone interface 55 | */ 56 | output wire [WB_ADDR_WIDTH-1:0] wb_adr_o, // ADR_O() address 57 | input wire [WB_DATA_WIDTH-1:0] wb_dat_i, // DAT_I() data in 58 | output wire [WB_DATA_WIDTH-1:0] wb_dat_o, // DAT_O() data out 59 | output wire wb_we_o, // WE_O write enable output 60 | output wire [WB_SELECT_WIDTH-1:0] wb_sel_o, // SEL_O() select output 61 | output wire wb_stb_o, // STB_O strobe output 62 | input wire wb_ack_i, // ACK_I acknowledge input 63 | input wire wb_err_i, // ERR_I error input 64 | output wire wb_cyc_o, // CYC_O cycle output 65 | 66 | /* 67 | * Status 68 | */ 69 | output wire busy, 70 | output wire bus_addressed, 71 | output wire bus_active, 72 | 73 | /* 74 | * Configuration 75 | */ 76 | input wire enable, 77 | input wire [6:0] device_address 78 | ); 79 | /* 80 | 81 | I2C 82 | 83 | Read 84 | __ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __ 85 | sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_\_R___A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A____/ 86 | ____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____ 87 | scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP 88 | 89 | Write 90 | __ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __ 91 | sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_/ W \_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_/ N \__/ 92 | ____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____ 93 | scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP 94 | 95 | Operation: 96 | 97 | This module enables I2C control over a Wishbone bus, useful for enabling a 98 | design to operate as a peripheral to an external microcontroller or similar. 99 | The Wishbone interface are fully parametrizable, with the restriction that the 100 | bus must be divided into 2**m words of 8*2**n bits. 101 | 102 | Writing via I2C first accesses an internal address register, followed by the 103 | actual wishbone bus. The first k bytes go to the address register, where 104 | 105 | k = ceil(log2(WB_ADDR_WIDTH+log2(WB_DATA_WIDTH/WB_SELECT_WIDTH))/8) 106 | 107 | . The address pointer will automatically increment with reads and writes. 108 | For buses with word size > 8 bits, the address register is in bytes and 109 | unaligned writes will be padded with zeros. Writes to the same bus address in 110 | the same I2C transaction are coalesced and written either once a complete 111 | word is ready or when the I2C transaction terminates with a stop or repeated 112 | start. 113 | 114 | Reading via the I2C interface immediately starts reading from the Wishbone 115 | interface starting from the current value of the internal address register. 116 | Like writes, reads are also coalesced when possible. One wishbone read is 117 | performed on the first I2C read. Once that has been completey transferred 118 | out, another read will be performed on the start of the next I2C read 119 | operation. 120 | 121 | Read 122 | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 123 | |_|_|_|_|_|_|_|_| |_|_|_|_|_|_|_|_|_|_ ... _|_|_|_|_|_|_|_|_|_| |_|_|_|_|_|_|_|_|___|_|_|_|_|_|_|_|_|_ ... _|_|_|_|_|_|_|_|_| |_| 124 | 125 | ST Device Addr W A Address MSB A Address LSB A RS Device Addr R A Data byte 0 A Data byte N N SP 126 | 127 | Write 128 | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 129 | |_|_|_|_|_|_|_|_| |_|_|_|_|_|_|_|_|_|_ ... _|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_ ... _|_|_|_|_|_|_|_|_|___| 130 | 131 | ST Device Addr W A Address MSB A Address LSB A Data byte 0 A Data byte N A SP 132 | 133 | Status: 134 | 135 | busy 136 | module is communicating over the bus 137 | 138 | bus_control 139 | module has control of bus in active state 140 | 141 | bus_active 142 | bus is active, not necessarily controlled by this module 143 | 144 | Parameters: 145 | 146 | device_address 147 | address of slave device 148 | 149 | Example of interfacing with tristate pins: 150 | (this will work for any tristate bus) 151 | 152 | assign scl_i = scl_pin; 153 | assign scl_pin = scl_t ? 1'bz : scl_o; 154 | assign sda_i = sda_pin; 155 | assign sda_pin = sda_t ? 1'bz : sda_o; 156 | 157 | Equivalent code that does not use *_t connections: 158 | (we can get away with this because I2C is open-drain) 159 | 160 | assign scl_i = scl_pin; 161 | assign scl_pin = scl_o ? 1'bz : 1'b0; 162 | assign sda_i = sda_pin; 163 | assign sda_pin = sda_o ? 1'bz : 1'b0; 164 | 165 | Example of two interconnected I2C devices: 166 | 167 | assign scl_1_i = scl_1_o & scl_2_o; 168 | assign scl_2_i = scl_1_o & scl_2_o; 169 | assign sda_1_i = sda_1_o & sda_2_o; 170 | assign sda_2_i = sda_1_o & sda_2_o; 171 | 172 | Example of two I2C devices sharing the same pins: 173 | 174 | assign scl_1_i = scl_pin; 175 | assign scl_2_i = scl_pin; 176 | assign scl_pin = (scl_1_o & scl_2_o) ? 1'bz : 1'b0; 177 | assign sda_1_i = sda_pin; 178 | assign sda_2_i = sda_pin; 179 | assign sda_pin = (sda_1_o & sda_2_o) ? 1'bz : 1'b0; 180 | 181 | Notes: 182 | 183 | scl_o should not be connected directly to scl_i, only via AND logic or a tristate 184 | I/O pin. This would prevent devices from stretching the clock period. 185 | 186 | */ 187 | 188 | // for interfaces that are more than one word wide, disable address lines 189 | parameter WB_VALID_ADDR_WIDTH = WB_ADDR_WIDTH - $clog2(WB_SELECT_WIDTH); 190 | // width of data port in words (1, 2, 4, or 8) 191 | parameter WB_WORD_WIDTH = WB_SELECT_WIDTH; 192 | // size of words (8, 16, 32, or 64 bits) 193 | parameter WB_WORD_SIZE = WB_DATA_WIDTH/WB_WORD_WIDTH; 194 | 195 | parameter WORD_PART_ADDR_WIDTH = $clog2(WB_WORD_SIZE/8); 196 | 197 | parameter ADDR_WIDTH_ADJ = WB_ADDR_WIDTH+WORD_PART_ADDR_WIDTH; 198 | 199 | parameter ADDR_WORD_WIDTH = (ADDR_WIDTH_ADJ+7)/8; 200 | 201 | // bus width assertions 202 | initial begin 203 | if (WB_WORD_WIDTH * WB_WORD_SIZE != WB_DATA_WIDTH) begin 204 | $error("Error: WB data width not evenly divisble"); 205 | $finish; 206 | end 207 | 208 | if (2**$clog2(WB_WORD_WIDTH) != WB_WORD_WIDTH) begin 209 | $error("Error: WB word width must be even power of two"); 210 | $finish; 211 | end 212 | 213 | if (8*2**$clog2(WB_WORD_SIZE/8) != WB_WORD_SIZE) begin 214 | $error("Error: WB word size must be a power of two multiple of 8 bits"); 215 | $finish; 216 | end 217 | end 218 | 219 | localparam [2:0] 220 | STATE_IDLE = 3'd0, 221 | STATE_ADDRESS = 3'd1, 222 | STATE_READ_1 = 3'd2, 223 | STATE_READ_2 = 3'd3, 224 | STATE_WRITE_1 = 3'd4, 225 | STATE_WRITE_2 = 3'd5; 226 | 227 | reg [2:0] state_reg = STATE_IDLE, state_next; 228 | 229 | reg [7:0] count_reg = 8'd0, count_next; 230 | reg last_cycle_reg = 1'b0; 231 | 232 | reg [ADDR_WIDTH_ADJ-1:0] addr_reg = {ADDR_WIDTH_ADJ{1'b0}}, addr_next; 233 | reg [WB_DATA_WIDTH-1:0] data_reg = {WB_DATA_WIDTH{1'b0}}, data_next; 234 | 235 | reg wb_we_o_reg = 1'b0, wb_we_o_next; 236 | reg [WB_SELECT_WIDTH-1:0] wb_sel_o_reg = {WB_SELECT_WIDTH{1'b0}}, wb_sel_o_next; 237 | reg wb_stb_o_reg = 1'b0, wb_stb_o_next; 238 | reg wb_cyc_o_reg = 1'b0, wb_cyc_o_next; 239 | 240 | reg busy_reg = 1'b0; 241 | 242 | reg [7:0] data_in_reg = 8'd0, data_in_next; 243 | reg data_in_valid_reg = 1'b0, data_in_valid_next; 244 | wire data_in_ready; 245 | 246 | wire [7:0] data_out; 247 | wire data_out_valid; 248 | wire data_out_last; 249 | reg data_out_ready_reg = 1'b0, data_out_ready_next; 250 | 251 | assign wb_adr_o = {addr_reg[ADDR_WIDTH_ADJ-1:ADDR_WIDTH_ADJ-WB_VALID_ADDR_WIDTH], {WB_ADDR_WIDTH-WB_VALID_ADDR_WIDTH{1'b0}}}; 252 | assign wb_dat_o = data_reg; 253 | assign wb_we_o = wb_we_o_reg; 254 | assign wb_sel_o = wb_sel_o_reg; 255 | assign wb_stb_o = wb_stb_o_reg; 256 | assign wb_cyc_o = wb_cyc_o_reg; 257 | 258 | assign busy = busy_reg; 259 | 260 | always @* begin 261 | state_next = STATE_IDLE; 262 | 263 | count_next = count_reg; 264 | 265 | data_in_next = 8'd0; 266 | data_in_valid_next = 1'b0; 267 | 268 | data_out_ready_next = 1'b0; 269 | 270 | addr_next = addr_reg; 271 | data_next = data_reg; 272 | 273 | wb_we_o_next = wb_we_o_reg; 274 | wb_sel_o_next = wb_sel_o_reg; 275 | wb_stb_o_next = 1'b0; 276 | wb_cyc_o_next = 1'b0; 277 | 278 | case (state_reg) 279 | STATE_IDLE: begin 280 | // idle, wait for I2C interface 281 | wb_we_o_next = 1'b0; 282 | 283 | if (data_out_valid) begin 284 | // store address and write 285 | count_next = ADDR_WORD_WIDTH-1; 286 | state_next = STATE_ADDRESS; 287 | end else if (data_in_ready & ~data_in_valid_reg) begin 288 | // read 289 | wb_cyc_o_next = 1'b1; 290 | wb_stb_o_next = 1'b1; 291 | wb_sel_o_next = {WB_SELECT_WIDTH{1'b1}}; 292 | state_next = STATE_READ_1; 293 | end 294 | end 295 | STATE_ADDRESS: begin 296 | // store address 297 | data_out_ready_next = 1'b1; 298 | 299 | if (data_out_ready_reg & data_out_valid) begin 300 | // store pointers 301 | addr_next[8*count_reg +: 8] = data_out; 302 | count_next = count_reg - 1; 303 | if (count_reg == 0) begin 304 | // end of header 305 | // set initial word offset 306 | if (WB_ADDR_WIDTH == WB_VALID_ADDR_WIDTH && WORD_PART_ADDR_WIDTH == 0) begin 307 | count_next = 0; 308 | end else begin 309 | count_next = addr_next[ADDR_WIDTH_ADJ-WB_VALID_ADDR_WIDTH-1:0]; 310 | end 311 | wb_sel_o_next = {WB_SELECT_WIDTH{1'b0}}; 312 | data_next = {WB_DATA_WIDTH{1'b0}}; 313 | if (data_out_last) begin 314 | // end of transaction 315 | state_next = STATE_IDLE; 316 | end else begin 317 | // start writing 318 | state_next = STATE_WRITE_1; 319 | end 320 | end else begin 321 | if (data_out_last) begin 322 | // end of transaction 323 | state_next = STATE_IDLE; 324 | end else begin 325 | state_next = STATE_ADDRESS; 326 | end 327 | end 328 | end else begin 329 | state_next = STATE_ADDRESS; 330 | end 331 | end 332 | STATE_READ_1: begin 333 | // wait for ack 334 | wb_cyc_o_next = 1'b1; 335 | wb_stb_o_next = 1'b1; 336 | 337 | if (wb_ack_i || wb_err_i) begin 338 | // read cycle complete, store result 339 | data_next = wb_dat_i; 340 | addr_next = addr_reg + (1 << (WB_ADDR_WIDTH-WB_VALID_ADDR_WIDTH+WORD_PART_ADDR_WIDTH)); 341 | wb_cyc_o_next = 1'b0; 342 | wb_stb_o_next = 1'b0; 343 | wb_sel_o_next = {WB_SELECT_WIDTH{1'b0}}; 344 | state_next = STATE_READ_2; 345 | end else begin 346 | state_next = STATE_READ_1; 347 | end 348 | end 349 | STATE_READ_2: begin 350 | // send data 351 | if (data_out_valid | !bus_addressed) begin 352 | // no longer addressed or now addressed for write, return to idle 353 | state_next = STATE_IDLE; 354 | end else if (data_in_ready & ~data_in_valid_reg) begin 355 | // transfer word and update pointers 356 | data_in_next = data_reg[8*count_reg +: 8]; 357 | data_in_valid_next = 1'b1; 358 | count_next = count_reg + 1; 359 | if (count_reg == (WB_SELECT_WIDTH*WB_WORD_SIZE/8)-1) begin 360 | // end of stored data word; return to idle 361 | count_next = 0; 362 | state_next = STATE_IDLE; 363 | end else begin 364 | state_next = STATE_READ_2; 365 | end 366 | end else begin 367 | state_next = STATE_READ_2; 368 | end 369 | end 370 | STATE_WRITE_1: begin 371 | // write data 372 | data_out_ready_next = 1'b1; 373 | 374 | if (data_out_ready_reg & data_out_valid) begin 375 | // store word 376 | data_next[8*count_reg +: 8] = data_out; 377 | count_next = count_reg + 1; 378 | wb_sel_o_next[count_reg >> ((WB_WORD_SIZE/8)-1)] = 1'b1; 379 | if (count_reg == (WB_SELECT_WIDTH*WB_WORD_SIZE/8)-1 || data_out_last) begin 380 | // have full word or at end of block, start write operation 381 | count_next = 0; 382 | wb_we_o_next = 1'b1; 383 | wb_cyc_o_next = 1'b1; 384 | wb_stb_o_next = 1'b1; 385 | state_next = STATE_WRITE_2; 386 | end else begin 387 | state_next = STATE_WRITE_1; 388 | end 389 | end else begin 390 | state_next = STATE_WRITE_1; 391 | end 392 | end 393 | STATE_WRITE_2: begin 394 | // wait for ack 395 | wb_cyc_o_next = 1'b1; 396 | wb_stb_o_next = 1'b1; 397 | 398 | if (wb_ack_i || wb_err_i) begin 399 | // end of write operation 400 | data_next = {WB_DATA_WIDTH{1'b0}}; 401 | addr_next = addr_reg + (1 << (WB_ADDR_WIDTH-WB_VALID_ADDR_WIDTH+WORD_PART_ADDR_WIDTH)); 402 | wb_cyc_o_next = 1'b0; 403 | wb_stb_o_next = 1'b0; 404 | wb_sel_o_next = {WB_SELECT_WIDTH{1'b0}}; 405 | if (last_cycle_reg) begin 406 | // end of transaction 407 | state_next = STATE_IDLE; 408 | end else begin 409 | state_next = STATE_WRITE_1; 410 | end 411 | end else begin 412 | state_next = STATE_WRITE_2; 413 | end 414 | end 415 | endcase 416 | end 417 | 418 | always @(posedge clk) begin 419 | state_reg <= state_next; 420 | 421 | count_reg <= count_next; 422 | 423 | if (data_out_ready_reg & data_out_valid) begin 424 | last_cycle_reg <= data_out_last; 425 | end 426 | 427 | addr_reg <= addr_next; 428 | data_reg <= data_next; 429 | 430 | wb_we_o_reg <= wb_we_o_next; 431 | wb_sel_o_reg <= wb_sel_o_next; 432 | wb_stb_o_reg <= wb_stb_o_next; 433 | wb_cyc_o_reg <= wb_cyc_o_next; 434 | 435 | busy_reg <= state_next != STATE_IDLE; 436 | 437 | data_in_reg <= data_in_next; 438 | data_in_valid_reg <= data_in_valid_next; 439 | 440 | data_out_ready_reg <= data_out_ready_next; 441 | 442 | if (rst) begin 443 | state_reg <= STATE_IDLE; 444 | data_in_valid_reg <= 1'b0; 445 | data_out_ready_reg <= 1'b0; 446 | wb_stb_o_reg <= 1'b0; 447 | wb_cyc_o_reg <= 1'b0; 448 | busy_reg <= 1'b0; 449 | end 450 | end 451 | 452 | i2c_slave #( 453 | .FILTER_LEN(FILTER_LEN) 454 | ) 455 | i2c_slave_inst ( 456 | .clk(clk), 457 | .rst(rst), 458 | 459 | // Host interface 460 | .release_bus(1'b0), 461 | 462 | .s_axis_data_tdata(data_in_reg), 463 | .s_axis_data_tvalid(data_in_valid_reg), 464 | .s_axis_data_tready(data_in_ready), 465 | .s_axis_data_tlast(1'b0), 466 | 467 | .m_axis_data_tdata(data_out), 468 | .m_axis_data_tvalid(data_out_valid), 469 | .m_axis_data_tready(data_out_ready_reg), 470 | .m_axis_data_tlast(data_out_last), 471 | 472 | // I2C Interface 473 | .scl_i(i2c_scl_i), 474 | .scl_o(i2c_scl_o), 475 | .scl_t(i2c_scl_t), 476 | .sda_i(i2c_sda_i), 477 | .sda_o(i2c_sda_o), 478 | .sda_t(i2c_sda_t), 479 | 480 | // Status 481 | .busy(), 482 | .bus_address(), 483 | .bus_addressed(bus_addressed), 484 | .bus_active(bus_active), 485 | 486 | // Configuration 487 | .enable(enable), 488 | .device_address(device_address), 489 | .device_address_mask(7'h7f) 490 | ); 491 | 492 | endmodule 493 | -------------------------------------------------------------------------------- /tb/i2c.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (c) 2015-2017 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | """ 24 | 25 | from myhdl import * 26 | import mmap 27 | 28 | class I2CMaster(object): 29 | def __init__(self): 30 | self.command_queue = [] 31 | self.read_data_queue = [] 32 | self.has_logic = False 33 | self.clk = None 34 | self.busy = False 35 | 36 | def init_read(self, address, length): 37 | self.command_queue.append(('r', address, length)) 38 | 39 | def init_write(self, address, data): 40 | self.command_queue.append(('w', address, data)) 41 | 42 | def idle(self): 43 | return len(self.command_queue) == 0 and not self.busy 44 | 45 | def wait(self): 46 | while not self.idle(): 47 | yield self.clk.posedge 48 | 49 | def read_data_ready(self): 50 | return len(self.read_data_queue) > 0 51 | 52 | def get_read_data(self): 53 | return self.read_data_queue.pop(0) 54 | 55 | def read(self, address, length): 56 | self.init_read(address, length) 57 | while not self.read_data_ready(): 58 | yield clk.posedge 59 | return self.get_read_data() 60 | 61 | def write(self, address, data): 62 | self.init_write(address, data) 63 | yield self.wait() 64 | 65 | def create_logic(self, 66 | clk, 67 | rst, 68 | scl_i, 69 | scl_o, 70 | scl_t, 71 | sda_i, 72 | sda_o, 73 | sda_t, 74 | prescale=2, 75 | name=None 76 | ): 77 | 78 | if self.has_logic: 79 | raise Exception("Logic already instantiated!") 80 | 81 | self.has_logic = True 82 | self.clk = clk 83 | 84 | line_state = [False] 85 | 86 | def send_start(): 87 | if line_state[0]: 88 | sda_o.next = 1 89 | sda_t.next = 1 90 | 91 | for i in range(prescale): 92 | yield clk.posedge 93 | 94 | scl_o.next = 1 95 | scl_t.next = 1 96 | 97 | while not scl_i: 98 | yield clk.posedge 99 | 100 | for i in range(prescale): 101 | yield clk.posedge 102 | 103 | sda_o.next = 0 104 | sda_t.next = 0 105 | 106 | for i in range(prescale): 107 | yield clk.posedge 108 | 109 | scl_o.next = 0 110 | scl_t.next = 0 111 | 112 | for i in range(prescale): 113 | yield clk.posedge 114 | 115 | line_state[0] = True 116 | 117 | def send_stop(): 118 | if not line_state[0]: 119 | return 120 | 121 | sda_o.next = 0 122 | sda_t.next = 0 123 | 124 | for i in range(prescale): 125 | yield clk.posedge 126 | 127 | scl_o.next = 1 128 | scl_t.next = 1 129 | 130 | while not scl_i: 131 | yield clk.posedge 132 | 133 | for i in range(prescale): 134 | yield clk.posedge 135 | 136 | sda_o.next = 1 137 | sda_t.next = 1 138 | 139 | for i in range(prescale): 140 | yield clk.posedge 141 | 142 | line_state[0] = False 143 | 144 | def send_bit(b): 145 | if not line_state[0]: 146 | send_start() 147 | 148 | sda_o.next = bool(b) 149 | sda_t.next = bool(b) 150 | 151 | for i in range(prescale): 152 | yield clk.posedge 153 | 154 | scl_o.next = 1 155 | scl_t.next = 1 156 | 157 | while not scl_i: 158 | yield clk.posedge 159 | 160 | for i in range(prescale*2): 161 | yield clk.posedge 162 | 163 | scl_o.next = 0 164 | scl_t.next = 0 165 | 166 | for i in range(prescale): 167 | yield clk.posedge 168 | 169 | 170 | def receive_bit(b): 171 | if len(b) == 0: 172 | b.append(0) 173 | 174 | if not line_state[0]: 175 | send_start() 176 | 177 | sda_o.next = 1 178 | sda_t.next = 1 179 | 180 | for i in range(prescale): 181 | yield clk.posedge 182 | 183 | scl_o.next = 1 184 | scl_t.next = 1 185 | 186 | while not scl_i: 187 | yield clk.posedge 188 | 189 | b[0] = int(sda_i) 190 | 191 | for i in range(prescale*2): 192 | yield clk.posedge 193 | 194 | scl_o.next = 0 195 | scl_t.next = 0 196 | 197 | for i in range(prescale): 198 | yield clk.posedge 199 | 200 | def send_byte(b, ack): 201 | for i in range(8): 202 | yield send_bit(b & (1 << 7-i)) 203 | yield receive_bit(ack) 204 | 205 | def receive_byte(b, ack): 206 | if len(b) == 0: 207 | b.append(0) 208 | b[0] = 0 209 | for i in range(8): 210 | v = [] 211 | yield receive_bit(v) 212 | b[0] = (b[0] << 1) | v[0] 213 | yield send_bit(ack) 214 | 215 | @instance 216 | def logic(): 217 | while True: 218 | yield clk.posedge 219 | self.busy = False 220 | 221 | # check for commands 222 | if len(self.command_queue) > 0: 223 | cmd = self.command_queue.pop(0) 224 | self.busy = True 225 | 226 | addr = cmd[1] 227 | 228 | if cmd[0] == 'w': 229 | # write command 230 | 231 | data = cmd[2] 232 | 233 | if name is not None: 234 | print("[%s] Write data a:0x%02x d:%s" % (name, addr, " ".join(("{:02x}".format(c) for c in bytearray(data))))) 235 | 236 | yield send_start() 237 | 238 | ack = [] 239 | yield send_byte(addr << 1 | 0, ack) 240 | 241 | if ack[0]: 242 | print("[%s] No ACK from slave" % name) 243 | 244 | for k in range(len(data)): 245 | ack = [] 246 | yield send_byte(data[k], ack) 247 | 248 | if ack[0]: 249 | print("[%s] No ACK from slave" % name) 250 | 251 | elif cmd[0] == 'r': 252 | # read command 253 | yield send_start() 254 | 255 | ack = [] 256 | yield send_byte(addr << 1 | 1, ack) 257 | 258 | if ack[0]: 259 | print("[%s] No ACK from slave" % name) 260 | 261 | cnt = cmd[2] 262 | data = b'' 263 | 264 | for k in range(cnt): 265 | d = [] 266 | yield receive_byte(d, k == cnt-1) 267 | data = data + bytearray([d[0]]) 268 | 269 | if name is not None: 270 | print("[%s] Read data a:0x%02x d:%s" % (name, addr, " ".join(("{:02x}".format(c) for c in bytearray(data))))) 271 | 272 | self.read_data_queue.append((addr, data)) 273 | 274 | else: 275 | # bad command; ignore it 276 | continue 277 | 278 | elif line_state[0]: 279 | # send stop 280 | yield send_stop() 281 | 282 | return instances() 283 | 284 | 285 | class I2CMem(object): 286 | def __init__(self, size = 1024): 287 | self.size = size 288 | self.mem = mmap.mmap(-1, size) 289 | self.has_logic = False 290 | 291 | def read_mem(self, address, length): 292 | self.mem.seek(address) 293 | return self.mem.read(length) 294 | 295 | def write_mem(self, address, data): 296 | self.mem.seek(address) 297 | self.mem.write(data) 298 | 299 | def create_logic(self, 300 | scl_i, 301 | scl_o, 302 | scl_t, 303 | sda_i, 304 | sda_o, 305 | sda_t, 306 | abw=2, 307 | address=0x50, 308 | latency=0, 309 | name=None 310 | ): 311 | 312 | if self.has_logic: 313 | raise Exception("Logic already instantiated!") 314 | 315 | self.has_logic = True 316 | 317 | def send_bit(b): 318 | if scl_i: 319 | yield scl_i.negedge 320 | 321 | sda_o.next = bool(b) 322 | sda_t.next = bool(b) 323 | 324 | if not scl_o: 325 | yield delay(10) 326 | 327 | scl_o.next = 1 328 | scl_t.next = 1 329 | 330 | yield scl_i.negedge 331 | 332 | sda_o.next = 1 333 | sda_t.next = 1 334 | 335 | def receive_bit(b): 336 | scl_o.next = 1 337 | scl_t.next = 1 338 | 339 | if len(b) == 0: 340 | b.append(0) 341 | 342 | sda_o.next = 1 343 | sda_t.next = 1 344 | 345 | if scl_i: 346 | yield scl_i.negedge, sda_i.posedge, sda_i.negedge 347 | 348 | if scl_i: 349 | # Got start or stop bit 350 | if sda_i: 351 | b[0] = 'stop' 352 | else: 353 | b[0] = 'start' 354 | return 355 | 356 | last_scl = int(scl_i) 357 | last_sda = int(sda_i) 358 | 359 | yield scl_i.posedge 360 | 361 | b[0] = int(sda_i) 362 | 363 | def send_byte(b, ack): 364 | for i in range(8): 365 | yield from send_bit(b & (1 << 7-i)) 366 | yield receive_bit(ack) 367 | 368 | def receive_byte(b, ack): 369 | if len(b) == 0: 370 | b.append(0) 371 | b[0] = 0 372 | for i in range(8): 373 | v = [] 374 | yield receive_bit(v) 375 | if type(v[0]) is str: 376 | b[0] = v[0] 377 | return 378 | b[0] = (b[0] << 1) | v[0] 379 | yield send_bit(ack) 380 | 381 | @instance 382 | def logic(): 383 | count = 0 384 | ptr = 0 385 | line_active = False 386 | 387 | while True: 388 | sda_o.next = 1 389 | sda_t.next = 1 390 | 391 | yield sda_i.negedge 392 | 393 | if scl_i: 394 | # start condition 395 | if name is not None: 396 | print("[%s] Got start bit" % name) 397 | 398 | line_active = True 399 | while line_active: 400 | # read address 401 | addr = 0 402 | for i in range(8): 403 | v = [] 404 | yield receive_bit(v) 405 | if type(v[0]) is str: 406 | addr = v[0] 407 | break 408 | else: 409 | addr = (addr << 1) | v[0] 410 | 411 | if addr == 'stop': 412 | # Stop bit 413 | if name is not None: 414 | print("[%s] Got stop bit" % name) 415 | line_active = False 416 | break 417 | elif addr == 'start': 418 | # Stop bit 419 | if name is not None: 420 | print("[%s] Got repeated start bit" % name) 421 | break 422 | 423 | rw = addr & 1 424 | addr = addr >> 1 425 | 426 | if addr == address: 427 | # address for me 428 | yield send_bit(0) 429 | 430 | if rw: 431 | # read 432 | if name is not None: 433 | print("[%s] Address matched (read)" % name) 434 | 435 | while True: 436 | if latency > 0: 437 | if scl_i: 438 | yield scl_i.negedge 439 | 440 | scl_o.next = 0 441 | scl_t.next = 0 442 | 443 | yield delay(latency) 444 | 445 | self.mem.seek(ptr) 446 | v = self.mem.read(1)[0] 447 | ack = [] 448 | 449 | yield send_byte(v, ack) 450 | 451 | if name is not None: 452 | print("[%s] Read data a:0x%0*x d:%02x" % (name, abw*2, ptr, v)) 453 | 454 | ptr = ptr + 1 455 | 456 | if ack[0]: 457 | if name is not None: 458 | print("[%s] Got NACK" % name) 459 | break 460 | else: 461 | # write 462 | if name is not None: 463 | print("[%s] Address matched (write)" % name) 464 | 465 | ptr = 0 466 | for k in range(abw): 467 | v = [] 468 | yield receive_byte(v, 0) 469 | ptr = (ptr << 8) | v[0] 470 | 471 | if name is not None: 472 | print("[%s] Set address pointer 0x%0*x" % (name, abw*2, ptr)) 473 | 474 | while True: 475 | if latency > 0: 476 | if scl_i: 477 | yield scl_i.negedge 478 | 479 | scl_o.next = 0 480 | scl_t.next = 0 481 | 482 | yield delay(latency) 483 | 484 | v = [] 485 | yield receive_byte(v, 0) 486 | if v[0] == 'stop': 487 | # Stop bit 488 | if name is not None: 489 | print("[%s] Got stop bit" % name) 490 | line_active = False 491 | break 492 | elif v[0] == 'start': 493 | # Repeated start 494 | if name is not None: 495 | print("[%s] Got repeated start bit" % name) 496 | break 497 | self.mem.seek(ptr) 498 | self.mem.write(bytes(bytearray([v[0]]))) 499 | 500 | if name is not None: 501 | print("[%s] Write data a:0x%0*x d:%02x" % (name, abw*2, ptr, v[0])) 502 | 503 | ptr = ptr + 1 504 | else: 505 | # no match, wait for start 506 | break 507 | 508 | return instances() 509 | 510 | 511 | -------------------------------------------------------------------------------- /tb/test_i2c.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | Copyright (c) 2015-2017 Alex Forencich 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | """ 25 | 26 | from myhdl import * 27 | import os 28 | 29 | import i2c 30 | 31 | def bench(): 32 | 33 | # Inputs 34 | clk = Signal(bool(0)) 35 | rst = Signal(bool(0)) 36 | current_test = Signal(intbv(0)[8:]) 37 | 38 | m_scl_i = Signal(bool(1)) 39 | m_sda_i = Signal(bool(1)) 40 | 41 | s1_scl_i = Signal(bool(1)) 42 | s1_sda_i = Signal(bool(1)) 43 | 44 | s2_scl_i = Signal(bool(1)) 45 | s2_sda_i = Signal(bool(1)) 46 | 47 | # Outputs 48 | m_scl_o = Signal(bool(1)) 49 | m_scl_t = Signal(bool(1)) 50 | m_sda_o = Signal(bool(1)) 51 | m_sda_t = Signal(bool(1)) 52 | 53 | s1_scl_o = Signal(bool(1)) 54 | s1_scl_t = Signal(bool(1)) 55 | s1_sda_o = Signal(bool(1)) 56 | s1_sda_t = Signal(bool(1)) 57 | 58 | s2_scl_o = Signal(bool(1)) 59 | s2_scl_t = Signal(bool(1)) 60 | s2_sda_o = Signal(bool(1)) 61 | s2_sda_t = Signal(bool(1)) 62 | 63 | # I2C master 64 | i2c_master_inst = i2c.I2CMaster() 65 | 66 | i2c_master_logic = i2c_master_inst.create_logic( 67 | clk, 68 | rst, 69 | scl_i=m_scl_i, 70 | scl_o=m_scl_o, 71 | scl_t=m_scl_t, 72 | sda_i=m_sda_i, 73 | sda_o=m_sda_o, 74 | sda_t=m_sda_t, 75 | prescale=2, 76 | name='master' 77 | ) 78 | 79 | # I2C memory model 1 80 | i2c_mem_inst1 = i2c.I2CMem(1024) 81 | 82 | i2c_mem_logic1 = i2c_mem_inst1.create_logic( 83 | scl_i=s1_scl_i, 84 | scl_o=s1_scl_o, 85 | scl_t=s1_scl_t, 86 | sda_i=s1_sda_i, 87 | sda_o=s1_sda_o, 88 | sda_t=s1_sda_t, 89 | abw=2, 90 | address=0x50, 91 | latency=0, 92 | name='slave1' 93 | ) 94 | 95 | # I2C memory model 2 96 | i2c_mem_inst2 = i2c.I2CMem(1024) 97 | 98 | i2c_mem_logic2 = i2c_mem_inst2.create_logic( 99 | scl_i=s2_scl_i, 100 | scl_o=s2_scl_o, 101 | scl_t=s2_scl_t, 102 | sda_i=s2_sda_i, 103 | sda_o=s2_sda_o, 104 | sda_t=s2_sda_t, 105 | abw=2, 106 | address=0x51, 107 | latency=1000, 108 | name='slave2' 109 | ) 110 | 111 | @always_comb 112 | def bus(): 113 | # emulate I2C wired AND 114 | m_scl_i.next = m_scl_o & s1_scl_o & s2_scl_o; 115 | m_sda_i.next = m_sda_o & s1_sda_o & s2_sda_o; 116 | 117 | s1_scl_i.next = m_scl_o & s1_scl_o & s2_scl_o; 118 | s1_sda_i.next = m_sda_o & s1_sda_o & s2_sda_o; 119 | 120 | s2_scl_i.next = m_scl_o & s1_scl_o & s2_scl_o; 121 | s2_sda_i.next = m_sda_o & s1_sda_o & s2_sda_o; 122 | 123 | @always(delay(4)) 124 | def clkgen(): 125 | clk.next = not clk 126 | 127 | @instance 128 | def check(): 129 | yield delay(100) 130 | yield clk.posedge 131 | rst.next = 1 132 | yield clk.posedge 133 | rst.next = 0 134 | yield clk.posedge 135 | yield delay(100) 136 | yield clk.posedge 137 | 138 | yield clk.posedge 139 | print("test 1: baseline") 140 | current_test.next = 1 141 | 142 | data = i2c_mem_inst1.read_mem(0, 32) 143 | for i in range(0, len(data), 16): 144 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 145 | 146 | yield delay(100) 147 | 148 | yield clk.posedge 149 | print("test 2: direct write") 150 | current_test.next = 2 151 | 152 | i2c_mem_inst1.write_mem(0, b'test') 153 | 154 | data = i2c_mem_inst1.read_mem(0, 32) 155 | for i in range(0, len(data), 16): 156 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 157 | 158 | assert i2c_mem_inst1.read_mem(0,4) == b'test' 159 | 160 | yield delay(100) 161 | 162 | yield clk.posedge 163 | print("test 3: write via I2C") 164 | current_test.next = 3 165 | 166 | i2c_master_inst.init_write(0x50, b'\x00\x04'+b'\x11\x22\x33\x44') 167 | 168 | yield i2c_master_inst.wait() 169 | yield clk.posedge 170 | 171 | data = i2c_mem_inst1.read_mem(0, 32) 172 | for i in range(0, len(data), 16): 173 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 174 | 175 | assert i2c_mem_inst1.read_mem(4,4) == b'\x11\x22\x33\x44' 176 | 177 | yield delay(100) 178 | 179 | yield clk.posedge 180 | print("test 4: read via I2C") 181 | current_test.next = 4 182 | 183 | i2c_master_inst.init_write(0x50, b'\x00\x04') 184 | i2c_master_inst.init_read(0x50, 4) 185 | 186 | yield i2c_master_inst.wait() 187 | yield clk.posedge 188 | 189 | data = i2c_master_inst.get_read_data() 190 | assert data[0] == 0x50 191 | assert data[1] == b'\x11\x22\x33\x44' 192 | 193 | yield delay(100) 194 | 195 | yield clk.posedge 196 | print("test 5: access slave 2") 197 | current_test.next = 3 198 | 199 | i2c_master_inst.init_write(0x51, b'\x00\x04'+b'\x11\x22\x33\x44') 200 | 201 | yield i2c_master_inst.wait() 202 | yield clk.posedge 203 | 204 | data = i2c_mem_inst2.read_mem(0, 32) 205 | for i in range(0, len(data), 16): 206 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 207 | 208 | assert i2c_mem_inst2.read_mem(4,4) == b'\x11\x22\x33\x44' 209 | 210 | i2c_master_inst.init_write(0x51, b'\x00\x04') 211 | i2c_master_inst.init_read(0x51, 4) 212 | 213 | yield i2c_master_inst.wait() 214 | yield clk.posedge 215 | 216 | data = i2c_master_inst.get_read_data() 217 | assert data[0] == 0x51 218 | assert data[1] == b'\x11\x22\x33\x44' 219 | 220 | yield delay(100) 221 | 222 | raise StopSimulation 223 | 224 | return i2c_master_logic, i2c_mem_logic1, i2c_mem_logic2, bus, clkgen, check 225 | 226 | def test_bench(): 227 | os.chdir(os.path.dirname(os.path.abspath(__file__))) 228 | #sim = Simulation(bench()) 229 | traceSignals.name = os.path.basename(__file__).rsplit('.',1)[0] 230 | sim = Simulation(traceSignals(bench)) 231 | sim.run() 232 | 233 | if __name__ == '__main__': 234 | print("Running test...") 235 | test_bench() 236 | 237 | -------------------------------------------------------------------------------- /tb/test_i2c_init.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | Copyright (c) 2015-2017 Alex Forencich 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | """ 25 | 26 | from myhdl import * 27 | import os 28 | 29 | import axis_ep 30 | 31 | module = 'i2c_init' 32 | testbench = 'test_%s' % module 33 | 34 | srcs = [] 35 | 36 | srcs.append("../rtl/%s.v" % module) 37 | srcs.append("%s.v" % testbench) 38 | 39 | src = ' '.join(srcs) 40 | 41 | build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) 42 | 43 | def bench(): 44 | 45 | # Parameters 46 | 47 | 48 | # Inputs 49 | clk = Signal(bool(0)) 50 | rst = Signal(bool(0)) 51 | current_test = Signal(intbv(0)[8:]) 52 | 53 | m_axis_cmd_ready = Signal(bool(0)) 54 | m_axis_data_tready = Signal(bool(0)) 55 | start = Signal(bool(0)) 56 | 57 | # Outputs 58 | m_axis_cmd_address = Signal(intbv(0)[7:]) 59 | m_axis_cmd_start = Signal(bool(0)) 60 | m_axis_cmd_read = Signal(bool(0)) 61 | m_axis_cmd_write = Signal(bool(0)) 62 | m_axis_cmd_write_multiple = Signal(bool(0)) 63 | m_axis_cmd_stop = Signal(bool(0)) 64 | m_axis_cmd_valid = Signal(bool(0)) 65 | m_axis_data_tdata = Signal(intbv(0)[8:]) 66 | m_axis_data_tvalid = Signal(bool(0)) 67 | m_axis_data_tlast = Signal(bool(1)) 68 | busy = Signal(bool(0)) 69 | 70 | # sources and sinks 71 | cmd_sink_pause = Signal(bool(0)) 72 | data_sink_pause = Signal(bool(0)) 73 | 74 | cmd_sink = axis_ep.AXIStreamSink() 75 | 76 | cmd_sink_logic = cmd_sink.create_logic( 77 | clk, 78 | rst, 79 | tdata=(m_axis_cmd_address, m_axis_cmd_start, m_axis_cmd_read, m_axis_cmd_write, m_axis_cmd_write_multiple, m_axis_cmd_stop), 80 | tvalid=m_axis_cmd_valid, 81 | tready=m_axis_cmd_ready, 82 | pause=cmd_sink_pause, 83 | name='cmd_sink' 84 | ) 85 | 86 | data_sink = axis_ep.AXIStreamSink() 87 | 88 | data_sink_logic = data_sink.create_logic( 89 | clk, 90 | rst, 91 | tdata=m_axis_data_tdata, 92 | tvalid=m_axis_data_tvalid, 93 | tready=m_axis_data_tready, 94 | tlast=m_axis_data_tlast, 95 | pause=data_sink_pause, 96 | name='data_sink' 97 | ) 98 | 99 | # DUT 100 | if os.system(build_cmd): 101 | raise Exception("Error running build command") 102 | 103 | dut = Cosimulation( 104 | "vvp -m myhdl %s.vvp -lxt2" % testbench, 105 | clk=clk, 106 | rst=rst, 107 | current_test=current_test, 108 | m_axis_cmd_address=m_axis_cmd_address, 109 | m_axis_cmd_start=m_axis_cmd_start, 110 | m_axis_cmd_read=m_axis_cmd_read, 111 | m_axis_cmd_write=m_axis_cmd_write, 112 | m_axis_cmd_write_multiple=m_axis_cmd_write_multiple, 113 | m_axis_cmd_stop=m_axis_cmd_stop, 114 | m_axis_cmd_valid=m_axis_cmd_valid, 115 | m_axis_cmd_ready=m_axis_cmd_ready, 116 | m_axis_data_tdata=m_axis_data_tdata, 117 | m_axis_data_tvalid=m_axis_data_tvalid, 118 | m_axis_data_tready=m_axis_data_tready, 119 | m_axis_data_tlast=m_axis_data_tlast, 120 | busy=busy, 121 | start=start 122 | ) 123 | 124 | @always(delay(4)) 125 | def clkgen(): 126 | clk.next = not clk 127 | 128 | @instance 129 | def check(): 130 | yield delay(100) 131 | yield clk.posedge 132 | rst.next = 1 133 | yield clk.posedge 134 | rst.next = 0 135 | yield clk.posedge 136 | yield delay(100) 137 | yield clk.posedge 138 | 139 | # testbench stimulus 140 | 141 | yield clk.posedge 142 | print("test 1: run, no delays") 143 | current_test.next = 1 144 | 145 | start.next = 1 146 | yield clk.posedge 147 | start.next = 0 148 | yield clk.posedge 149 | yield clk.posedge 150 | 151 | while busy: 152 | yield clk.posedge 153 | 154 | # addresses and data for checking 155 | addr = [0x50, 0x50, 0x51, 0x52, 0x53] 156 | data = [0x00, 0x04, 0x11, 0x22, 0x33, 0x44] 157 | 158 | # check all issued commands 159 | for a in addr: 160 | first = True 161 | for d in data: 162 | f1 = cmd_sink.recv() 163 | f2 = data_sink.recv() 164 | assert f1.data[0][0] == a # address 165 | assert f1.data[0][1] == first # start 166 | assert f1.data[0][2] == 0 # read 167 | assert f1.data[0][3] == 1 # write 168 | assert f1.data[0][4] == 0 # write multiple 169 | assert f1.data[0][5] == 0 # stop 170 | assert f2.data[0] == d 171 | first = False 172 | 173 | # check for stop command 174 | f1 = cmd_sink.recv() 175 | assert f1.data[0][1] == 0 # start 176 | assert f1.data[0][2] == 0 # read 177 | assert f1.data[0][3] == 0 # write 178 | assert f1.data[0][4] == 0 # write multiple 179 | assert f1.data[0][5] == 1 # stop 180 | 181 | # make sure we got everything 182 | assert cmd_sink.empty() 183 | assert data_sink.empty() 184 | 185 | yield delay(100) 186 | 187 | # testbench stimulus 188 | 189 | yield clk.posedge 190 | print("test 2: run with delays") 191 | current_test.next = 2 192 | 193 | start.next = 1 194 | yield clk.posedge 195 | start.next = 0 196 | yield clk.posedge 197 | yield clk.posedge 198 | 199 | cmd_sink_pause.next = 0 200 | data_sink_pause.next = 1 201 | 202 | while busy: 203 | yield delay(100) 204 | yield clk.posedge 205 | cmd_sink_pause.next = 0 206 | data_sink_pause.next = 1 207 | yield clk.posedge 208 | cmd_sink_pause.next = 1 209 | data_sink_pause.next = 1 210 | yield delay(100) 211 | yield clk.posedge 212 | cmd_sink_pause.next = 1 213 | data_sink_pause.next = 0 214 | yield clk.posedge 215 | cmd_sink_pause.next = 1 216 | data_sink_pause.next = 1 217 | 218 | cmd_sink_pause.next = 0 219 | data_sink_pause.next = 0 220 | 221 | # addresses and data for checking 222 | addr = [0x50, 0x50, 0x51, 0x52, 0x53] 223 | data = [0x00, 0x04, 0x11, 0x22, 0x33, 0x44] 224 | 225 | # check all issued commands 226 | for a in addr: 227 | first = True 228 | for d in data: 229 | f1 = cmd_sink.recv() 230 | f2 = data_sink.recv() 231 | assert f1.data[0][0] == a # address 232 | assert f1.data[0][1] == first # start 233 | assert f1.data[0][2] == 0 # read 234 | assert f1.data[0][3] == 1 # write 235 | assert f1.data[0][4] == 0 # write multiple 236 | assert f1.data[0][5] == 0 # stop 237 | assert f2.data[0] == d 238 | first = False 239 | 240 | # check for stop command 241 | f1 = cmd_sink.recv() 242 | assert f1.data[0][1] == 0 # start 243 | assert f1.data[0][2] == 0 # read 244 | assert f1.data[0][3] == 0 # write 245 | assert f1.data[0][4] == 0 # write multiple 246 | assert f1.data[0][5] == 1 # stop 247 | 248 | # make sure we got everything 249 | assert cmd_sink.empty() 250 | assert data_sink.empty() 251 | 252 | yield delay(100) 253 | 254 | raise StopSimulation 255 | 256 | return instances() 257 | 258 | def test_bench(): 259 | sim = Simulation(bench()) 260 | sim.run() 261 | 262 | if __name__ == '__main__': 263 | print("Running test...") 264 | test_bench() 265 | -------------------------------------------------------------------------------- /tb/test_i2c_init.v: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015-2017 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | // Language: Verilog 2001 26 | 27 | `timescale 1ns / 1ps 28 | 29 | /* 30 | * Testbench for i2c_init 31 | */ 32 | module test_i2c_init; 33 | 34 | // Parameters 35 | 36 | // Inputs 37 | reg clk = 0; 38 | reg rst = 0; 39 | reg [7:0] current_test = 0; 40 | 41 | reg m_axis_cmd_ready = 0; 42 | reg m_axis_data_tready = 0; 43 | reg start = 0; 44 | 45 | // Outputs 46 | wire [6:0] m_axis_cmd_address; 47 | wire m_axis_cmd_start; 48 | wire m_axis_cmd_read; 49 | wire m_axis_cmd_write; 50 | wire m_axis_cmd_write_multiple; 51 | wire m_axis_cmd_stop; 52 | wire m_axis_cmd_valid; 53 | wire [7:0] m_axis_data_tdata; 54 | wire m_axis_data_tvalid; 55 | wire m_axis_data_tlast; 56 | wire busy; 57 | 58 | initial begin 59 | // myhdl integration 60 | $from_myhdl( 61 | clk, 62 | rst, 63 | current_test, 64 | m_axis_cmd_ready, 65 | m_axis_data_tready, 66 | start); 67 | $to_myhdl( 68 | m_axis_cmd_address, 69 | m_axis_cmd_start, 70 | m_axis_cmd_read, 71 | m_axis_cmd_write, 72 | m_axis_cmd_write_multiple, 73 | m_axis_cmd_stop, 74 | m_axis_cmd_valid, 75 | m_axis_data_tdata, 76 | m_axis_data_tvalid, 77 | m_axis_data_tlast, 78 | busy 79 | ); 80 | 81 | // dump file 82 | $dumpfile("test_i2c_init.lxt"); 83 | $dumpvars(0, test_i2c_init); 84 | end 85 | 86 | i2c_init 87 | UUT ( 88 | .clk(clk), 89 | .rst(rst), 90 | .m_axis_cmd_address(m_axis_cmd_address), 91 | .m_axis_cmd_start(m_axis_cmd_start), 92 | .m_axis_cmd_read(m_axis_cmd_read), 93 | .m_axis_cmd_write(m_axis_cmd_write), 94 | .m_axis_cmd_write_multiple(m_axis_cmd_write_multiple), 95 | .m_axis_cmd_stop(m_axis_cmd_stop), 96 | .m_axis_cmd_valid(m_axis_cmd_valid), 97 | .m_axis_cmd_ready(m_axis_cmd_ready), 98 | .m_axis_data_tdata(m_axis_data_tdata), 99 | .m_axis_data_tvalid(m_axis_data_tvalid), 100 | .m_axis_data_tready(m_axis_data_tready), 101 | .m_axis_data_tlast(m_axis_data_tlast), 102 | .busy(busy), 103 | .start(start) 104 | ); 105 | 106 | endmodule 107 | -------------------------------------------------------------------------------- /tb/test_i2c_master.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | Copyright (c) 2015-2017 Alex Forencich 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | """ 25 | 26 | from myhdl import * 27 | import os 28 | 29 | import axis_ep 30 | import i2c 31 | 32 | module = 'i2c_master' 33 | testbench = 'test_%s' % module 34 | 35 | srcs = [] 36 | 37 | srcs.append("../rtl/%s.v" % module) 38 | srcs.append("%s.v" % testbench) 39 | 40 | src = ' '.join(srcs) 41 | 42 | build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) 43 | 44 | def bench(): 45 | 46 | # Parameters 47 | 48 | 49 | # Inputs 50 | clk = Signal(bool(0)) 51 | rst = Signal(bool(0)) 52 | current_test = Signal(intbv(0)[8:]) 53 | 54 | s_axis_cmd_address = Signal(intbv(0)[7:]) 55 | s_axis_cmd_start = Signal(bool(0)) 56 | s_axis_cmd_read = Signal(bool(0)) 57 | s_axis_cmd_write = Signal(bool(0)) 58 | s_axis_cmd_write_multiple = Signal(bool(0)) 59 | s_axis_cmd_stop = Signal(bool(0)) 60 | s_axis_cmd_valid = Signal(bool(0)) 61 | s_axis_data_tdata = Signal(intbv(0)[8:]) 62 | s_axis_data_tvalid = Signal(bool(0)) 63 | s_axis_data_tlast = Signal(bool(0)) 64 | m_axis_data_tready = Signal(bool(0)) 65 | scl_i = Signal(bool(1)) 66 | sda_i = Signal(bool(1)) 67 | prescale = Signal(intbv(0)[16:]) 68 | stop_on_idle = Signal(bool(0)) 69 | 70 | s1_scl_i = Signal(bool(1)) 71 | s1_sda_i = Signal(bool(1)) 72 | 73 | s2_scl_i = Signal(bool(1)) 74 | s2_sda_i = Signal(bool(1)) 75 | 76 | # Outputs 77 | s_axis_cmd_ready = Signal(bool(0)) 78 | s_axis_data_tready = Signal(bool(0)) 79 | m_axis_data_tdata = Signal(intbv(0)[8:]) 80 | m_axis_data_tvalid = Signal(bool(0)) 81 | m_axis_data_tlast = Signal(bool(0)) 82 | scl_o = Signal(bool(1)) 83 | scl_t = Signal(bool(1)) 84 | sda_o = Signal(bool(1)) 85 | sda_t = Signal(bool(1)) 86 | busy = Signal(bool(0)) 87 | bus_control = Signal(bool(0)) 88 | bus_active = Signal(bool(0)) 89 | missed_ack = Signal(bool(0)) 90 | 91 | s1_scl_o = Signal(bool(1)) 92 | s1_scl_t = Signal(bool(1)) 93 | s1_sda_o = Signal(bool(1)) 94 | s1_sda_t = Signal(bool(1)) 95 | 96 | s2_scl_o = Signal(bool(1)) 97 | s2_scl_t = Signal(bool(1)) 98 | s2_sda_o = Signal(bool(1)) 99 | s2_sda_t = Signal(bool(1)) 100 | 101 | # sources and sinks 102 | cmd_source_pause = Signal(bool(0)) 103 | data_source_pause = Signal(bool(0)) 104 | data_sink_pause = Signal(bool(0)) 105 | 106 | cmd_source = axis_ep.AXIStreamSource() 107 | 108 | cmd_source_logic = cmd_source.create_logic( 109 | clk, 110 | rst, 111 | tdata=(s_axis_cmd_address, s_axis_cmd_start, s_axis_cmd_read, s_axis_cmd_write, s_axis_cmd_write_multiple, s_axis_cmd_stop), 112 | tvalid=s_axis_cmd_valid, 113 | tready=s_axis_cmd_ready, 114 | pause=cmd_source_pause, 115 | name='cmd_source' 116 | ) 117 | 118 | data_source = axis_ep.AXIStreamSource() 119 | 120 | data_source_logic = data_source.create_logic( 121 | clk, 122 | rst, 123 | tdata=s_axis_data_tdata, 124 | tvalid=s_axis_data_tvalid, 125 | tready=s_axis_data_tready, 126 | tlast=s_axis_data_tlast, 127 | pause=data_source_pause, 128 | name='data_source' 129 | ) 130 | 131 | data_sink = axis_ep.AXIStreamSink() 132 | 133 | data_sink_logic = data_sink.create_logic( 134 | clk, 135 | rst, 136 | tdata=m_axis_data_tdata, 137 | tvalid=m_axis_data_tvalid, 138 | tready=m_axis_data_tready, 139 | tlast=m_axis_data_tlast, 140 | pause=data_sink_pause, 141 | name='data_sink' 142 | ) 143 | 144 | # I2C memory model 1 145 | i2c_mem_inst1 = i2c.I2CMem(1024) 146 | 147 | i2c_mem_logic1 = i2c_mem_inst1.create_logic( 148 | scl_i=s1_scl_i, 149 | scl_o=s1_scl_o, 150 | scl_t=s1_scl_t, 151 | sda_i=s1_sda_i, 152 | sda_o=s1_sda_o, 153 | sda_t=s1_sda_t, 154 | abw=2, 155 | address=0x50, 156 | latency=0, 157 | name='slave1' 158 | ) 159 | 160 | # I2C memory model 2 161 | i2c_mem_inst2 = i2c.I2CMem(1024) 162 | 163 | i2c_mem_logic2 = i2c_mem_inst2.create_logic( 164 | scl_i=s2_scl_i, 165 | scl_o=s2_scl_o, 166 | scl_t=s2_scl_t, 167 | sda_i=s2_sda_i, 168 | sda_o=s2_sda_o, 169 | sda_t=s2_sda_t, 170 | abw=2, 171 | address=0x51, 172 | latency=1000, 173 | name='slave2' 174 | ) 175 | 176 | # DUT 177 | if os.system(build_cmd): 178 | raise Exception("Error running build command") 179 | 180 | dut = Cosimulation( 181 | "vvp -m myhdl %s.vvp -lxt2" % testbench, 182 | clk=clk, 183 | rst=rst, 184 | current_test=current_test, 185 | 186 | s_axis_cmd_address=s_axis_cmd_address, 187 | s_axis_cmd_start=s_axis_cmd_start, 188 | s_axis_cmd_read=s_axis_cmd_read, 189 | s_axis_cmd_write=s_axis_cmd_write, 190 | s_axis_cmd_write_multiple=s_axis_cmd_write_multiple, 191 | s_axis_cmd_stop=s_axis_cmd_stop, 192 | s_axis_cmd_valid=s_axis_cmd_valid, 193 | s_axis_cmd_ready=s_axis_cmd_ready, 194 | 195 | s_axis_data_tdata=s_axis_data_tdata, 196 | s_axis_data_tvalid=s_axis_data_tvalid, 197 | s_axis_data_tready=s_axis_data_tready, 198 | s_axis_data_tlast=s_axis_data_tlast, 199 | 200 | m_axis_data_tdata=m_axis_data_tdata, 201 | m_axis_data_tvalid=m_axis_data_tvalid, 202 | m_axis_data_tready=m_axis_data_tready, 203 | m_axis_data_tlast=m_axis_data_tlast, 204 | 205 | scl_i=scl_i, 206 | scl_o=scl_o, 207 | scl_t=scl_t, 208 | sda_i=sda_i, 209 | sda_o=sda_o, 210 | sda_t=sda_t, 211 | 212 | busy=busy, 213 | bus_control=bus_control, 214 | bus_active=bus_active, 215 | missed_ack=missed_ack, 216 | 217 | prescale=prescale, 218 | stop_on_idle=stop_on_idle 219 | ) 220 | 221 | @always_comb 222 | def bus(): 223 | # emulate I2C wired AND 224 | scl_i.next = scl_o & s1_scl_o & s2_scl_o; 225 | sda_i.next = sda_o & s1_sda_o & s2_sda_o; 226 | 227 | s1_scl_i.next = scl_o & s1_scl_o & s2_scl_o; 228 | s1_sda_i.next = sda_o & s1_sda_o & s2_sda_o; 229 | 230 | s2_scl_i.next = scl_o & s1_scl_o & s2_scl_o; 231 | s2_sda_i.next = sda_o & s1_sda_o & s2_sda_o; 232 | 233 | @always(delay(4)) 234 | def clkgen(): 235 | clk.next = not clk 236 | 237 | @instance 238 | def check(): 239 | yield delay(100) 240 | yield clk.posedge 241 | rst.next = 1 242 | yield clk.posedge 243 | rst.next = 0 244 | yield clk.posedge 245 | yield delay(100) 246 | yield clk.posedge 247 | 248 | prescale.next = 2 249 | 250 | yield clk.posedge 251 | 252 | # testbench stimulus 253 | 254 | yield clk.posedge 255 | print("test 1: write") 256 | current_test.next = 1 257 | 258 | cmd_source.send([( 259 | 0x50, # address 260 | 0, # start 261 | 0, # read 262 | 0, # write 263 | 1, # write_multiple 264 | 1 # stop 265 | )]) 266 | data_source.send((b'\x00\x04'+b'\x11\x22\x33\x44')) 267 | 268 | yield clk.posedge 269 | yield clk.posedge 270 | yield clk.posedge 271 | while busy or bus_active or not cmd_source.empty(): 272 | yield clk.posedge 273 | yield clk.posedge 274 | 275 | data = i2c_mem_inst1.read_mem(0, 32) 276 | for i in range(0, len(data), 16): 277 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 278 | 279 | assert i2c_mem_inst1.read_mem(4,4) == b'\x11\x22\x33\x44' 280 | 281 | yield delay(100) 282 | 283 | yield clk.posedge 284 | print("test 2: read") 285 | current_test.next = 2 286 | 287 | cmd_source.send([( 288 | 0x50, # address 289 | 0, # start 290 | 0, # read 291 | 0, # write 292 | 1, # write_multiple 293 | 0 # stop 294 | )]) 295 | data_source.send((b'\x00\x04')) 296 | 297 | for i in range(3): 298 | cmd_source.send([( 299 | 0x50, # address 300 | 0, # start 301 | 1, # read 302 | 0, # write 303 | 0, # write_multiple 304 | 0 # stop 305 | )]) 306 | 307 | cmd_source.send([( 308 | 0x50, # address 309 | 0, # start 310 | 1, # read 311 | 0, # write 312 | 0, # write_multiple 313 | 1 # stop 314 | )]) 315 | 316 | yield clk.posedge 317 | yield clk.posedge 318 | yield clk.posedge 319 | while busy or bus_active or not cmd_source.empty(): 320 | yield clk.posedge 321 | yield clk.posedge 322 | 323 | data = data_sink.recv() 324 | assert data.data == b'\x11\x22\x33\x44' 325 | 326 | yield delay(100) 327 | 328 | yield clk.posedge 329 | print("test 3: write to slave 2") 330 | current_test.next = 3 331 | 332 | cmd_source.send([( 333 | 0x51, # address 334 | 0, # start 335 | 0, # read 336 | 0, # write 337 | 1, # write_multiple 338 | 1 # stop 339 | )]) 340 | data_source.send((b'\x00\x04'+b'\x44\x33\x22\x11')) 341 | 342 | yield clk.posedge 343 | yield clk.posedge 344 | yield clk.posedge 345 | while busy or bus_active or not cmd_source.empty(): 346 | yield clk.posedge 347 | yield clk.posedge 348 | 349 | data = i2c_mem_inst1.read_mem(0, 32) 350 | for i in range(0, len(data), 16): 351 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 352 | 353 | assert i2c_mem_inst2.read_mem(4,4) == b'\x44\x33\x22\x11' 354 | 355 | yield delay(100) 356 | 357 | yield clk.posedge 358 | print("test 4: read from slave 2") 359 | current_test.next = 4 360 | 361 | cmd_source.send([( 362 | 0x51, # address 363 | 0, # start 364 | 0, # read 365 | 0, # write 366 | 1, # write_multiple 367 | 0 # stop 368 | )]) 369 | data_source.send((b'\x00\x04')) 370 | 371 | for i in range(3): 372 | cmd_source.send([( 373 | 0x51, # address 374 | 0, # start 375 | 1, # read 376 | 0, # write 377 | 0, # write_multiple 378 | 0 # stop 379 | )]) 380 | 381 | cmd_source.send([( 382 | 0x51, # address 383 | 0, # start 384 | 1, # read 385 | 0, # write 386 | 0, # write_multiple 387 | 1 # stop 388 | )]) 389 | 390 | yield clk.posedge 391 | yield clk.posedge 392 | yield clk.posedge 393 | while busy or bus_active or not cmd_source.empty(): 394 | yield clk.posedge 395 | yield clk.posedge 396 | 397 | data = data_sink.recv() 398 | assert data.data == b'\x44\x33\x22\x11' 399 | 400 | yield delay(100) 401 | 402 | yield clk.posedge 403 | print("test 5: write to nonexistent device") 404 | current_test.next = 5 405 | 406 | cmd_source.send([( 407 | 0x52, # address 408 | 0, # start 409 | 0, # read 410 | 0, # write 411 | 1, # write_multiple 412 | 1 # stop 413 | )]) 414 | data_source.send((b'\x00\x04'+b'\xde\xad\xbe\xef')) 415 | 416 | got_missed_ack = False 417 | 418 | for k in range(1000): 419 | got_missed_ack |= missed_ack 420 | yield clk.posedge 421 | 422 | assert got_missed_ack 423 | 424 | yield clk.posedge 425 | yield clk.posedge 426 | yield clk.posedge 427 | while busy or bus_active or not cmd_source.empty(): 428 | yield clk.posedge 429 | yield clk.posedge 430 | 431 | yield delay(100) 432 | 433 | raise StopSimulation 434 | 435 | return instances() 436 | 437 | def test_bench(): 438 | sim = Simulation(bench()) 439 | sim.run() 440 | 441 | if __name__ == '__main__': 442 | print("Running test...") 443 | test_bench() 444 | -------------------------------------------------------------------------------- /tb/test_i2c_master.v: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015-2017 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | // Language: Verilog 2001 26 | 27 | `timescale 1ns / 1ps 28 | 29 | /* 30 | * Testbench for i2c_master 31 | */ 32 | module test_i2c_master; 33 | 34 | // Parameters 35 | 36 | // Inputs 37 | reg clk = 0; 38 | reg rst = 0; 39 | reg [7:0] current_test = 0; 40 | 41 | reg [6:0] s_axis_cmd_address = 0; 42 | reg s_axis_cmd_start = 0; 43 | reg s_axis_cmd_read = 0; 44 | reg s_axis_cmd_write = 0; 45 | reg s_axis_cmd_write_multiple = 0; 46 | reg s_axis_cmd_stop = 0; 47 | reg s_axis_cmd_valid = 0; 48 | reg [7:0] s_axis_data_tdata = 0; 49 | reg s_axis_data_tvalid = 0; 50 | reg s_axis_data_tlast = 0; 51 | reg m_axis_data_tready = 0; 52 | reg scl_i = 1; 53 | reg sda_i = 1; 54 | reg [15:0] prescale = 0; 55 | reg stop_on_idle = 0; 56 | 57 | // Outputs 58 | wire s_axis_cmd_ready; 59 | wire s_axis_data_tready; 60 | wire [7:0] m_axis_data_tdata; 61 | wire m_axis_data_tvalid; 62 | wire m_axis_data_tlast; 63 | wire scl_o; 64 | wire scl_t; 65 | wire sda_o; 66 | wire sda_t; 67 | wire busy; 68 | wire bus_control; 69 | wire bus_active; 70 | wire missed_ack; 71 | 72 | initial begin 73 | // myhdl integration 74 | $from_myhdl( 75 | clk, 76 | rst, 77 | current_test, 78 | s_axis_cmd_address, 79 | s_axis_cmd_start, 80 | s_axis_cmd_read, 81 | s_axis_cmd_write, 82 | s_axis_cmd_write_multiple, 83 | s_axis_cmd_stop, 84 | s_axis_cmd_valid, 85 | s_axis_data_tdata, 86 | s_axis_data_tvalid, 87 | s_axis_data_tlast, 88 | m_axis_data_tready, 89 | scl_i, 90 | sda_i, 91 | prescale, 92 | stop_on_idle 93 | ); 94 | $to_myhdl( 95 | s_axis_cmd_ready, 96 | s_axis_data_tready, 97 | m_axis_data_tdata, 98 | m_axis_data_tvalid, 99 | m_axis_data_tlast, 100 | scl_o, 101 | scl_t, 102 | sda_o, 103 | sda_t, 104 | busy, 105 | bus_control, 106 | bus_active, 107 | missed_ack 108 | ); 109 | 110 | // dump file 111 | $dumpfile("test_i2c_master.lxt"); 112 | $dumpvars(0, test_i2c_master); 113 | end 114 | 115 | i2c_master 116 | UUT ( 117 | .clk(clk), 118 | .rst(rst), 119 | .s_axis_cmd_address(s_axis_cmd_address), 120 | .s_axis_cmd_start(s_axis_cmd_start), 121 | .s_axis_cmd_read(s_axis_cmd_read), 122 | .s_axis_cmd_write(s_axis_cmd_write), 123 | .s_axis_cmd_write_multiple(s_axis_cmd_write_multiple), 124 | .s_axis_cmd_stop(s_axis_cmd_stop), 125 | .s_axis_cmd_valid(s_axis_cmd_valid), 126 | .s_axis_cmd_ready(s_axis_cmd_ready), 127 | .s_axis_data_tdata(s_axis_data_tdata), 128 | .s_axis_data_tvalid(s_axis_data_tvalid), 129 | .s_axis_data_tready(s_axis_data_tready), 130 | .s_axis_data_tlast(s_axis_data_tlast), 131 | .m_axis_data_tdata(m_axis_data_tdata), 132 | .m_axis_data_tvalid(m_axis_data_tvalid), 133 | .m_axis_data_tready(m_axis_data_tready), 134 | .m_axis_data_tlast(m_axis_data_tlast), 135 | .scl_i(scl_i), 136 | .scl_o(scl_o), 137 | .scl_t(scl_t), 138 | .sda_i(sda_i), 139 | .sda_o(sda_o), 140 | .sda_t(sda_t), 141 | .busy(busy), 142 | .bus_control(bus_control), 143 | .bus_active(bus_active), 144 | .missed_ack(missed_ack), 145 | .prescale(prescale), 146 | .stop_on_idle(stop_on_idle) 147 | ); 148 | 149 | endmodule 150 | -------------------------------------------------------------------------------- /tb/test_i2c_master_axil.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | Copyright (c) 2019 Alex Forencich 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | """ 25 | 26 | from myhdl import * 27 | import os 28 | 29 | import i2c 30 | import axil 31 | 32 | module = 'i2c_master_axil' 33 | testbench = 'test_%s' % module 34 | 35 | srcs = [] 36 | 37 | srcs.append("../rtl/%s.v" % module) 38 | srcs.append("../rtl/i2c_master.v") 39 | srcs.append("../rtl/axis_fifo.v") 40 | srcs.append("%s.v" % testbench) 41 | 42 | src = ' '.join(srcs) 43 | 44 | build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) 45 | 46 | def bench(): 47 | 48 | # Parameters 49 | DEFAULT_PRESCALE = 1 50 | FIXED_PRESCALE = 0 51 | CMD_FIFO = 1 52 | CMD_FIFO_DEPTH = 32 53 | WRITE_FIFO = 1 54 | WRITE_FIFO_DEPTH = 32 55 | READ_FIFO = 1 56 | READ_FIFO_DEPTH = 32 57 | 58 | # Inputs 59 | clk = Signal(bool(0)) 60 | rst = Signal(bool(0)) 61 | current_test = Signal(intbv(0)[8:]) 62 | 63 | s_axil_awaddr = Signal(intbv(0)[4:]) 64 | s_axil_awprot = Signal(intbv(0)[3:]) 65 | s_axil_awvalid = Signal(bool(0)) 66 | s_axil_wdata = Signal(intbv(0)[32:]) 67 | s_axil_wstrb = Signal(intbv(0)[4:]) 68 | s_axil_wvalid = Signal(bool(0)) 69 | s_axil_bready = Signal(bool(0)) 70 | s_axil_araddr = Signal(intbv(0)[4:]) 71 | s_axil_arprot = Signal(intbv(0)[3:]) 72 | s_axil_arvalid = Signal(bool(0)) 73 | s_axil_rready = Signal(bool(0)) 74 | i2c_scl_i = Signal(bool(1)) 75 | i2c_sda_i = Signal(bool(1)) 76 | 77 | s1_scl_i = Signal(bool(1)) 78 | s1_sda_i = Signal(bool(1)) 79 | 80 | s2_scl_i = Signal(bool(1)) 81 | s2_sda_i = Signal(bool(1)) 82 | 83 | # Outputs 84 | s_axil_awready = Signal(bool(0)) 85 | s_axil_wready = Signal(bool(0)) 86 | s_axil_bresp = Signal(intbv(0)[2:]) 87 | s_axil_bvalid = Signal(bool(0)) 88 | s_axil_arready = Signal(bool(0)) 89 | s_axil_rdata = Signal(intbv(0)[32:]) 90 | s_axil_rresp = Signal(intbv(0)[2:]) 91 | s_axil_rvalid = Signal(bool(0)) 92 | i2c_scl_o = Signal(bool(1)) 93 | i2c_scl_t = Signal(bool(1)) 94 | i2c_sda_o = Signal(bool(1)) 95 | i2c_sda_t = Signal(bool(1)) 96 | 97 | s1_scl_o = Signal(bool(1)) 98 | s1_scl_t = Signal(bool(1)) 99 | s1_sda_o = Signal(bool(1)) 100 | s1_sda_t = Signal(bool(1)) 101 | 102 | s2_scl_o = Signal(bool(1)) 103 | s2_scl_t = Signal(bool(1)) 104 | s2_sda_o = Signal(bool(1)) 105 | s2_sda_t = Signal(bool(1)) 106 | 107 | # AXI4-Lite master 108 | axil_master_inst = axil.AXILiteMaster() 109 | axil_master_pause = Signal(bool(False)) 110 | 111 | axil_master_logic = axil_master_inst.create_logic( 112 | clk, 113 | rst, 114 | m_axil_awaddr=s_axil_awaddr, 115 | m_axil_awprot=s_axil_awprot, 116 | m_axil_awvalid=s_axil_awvalid, 117 | m_axil_awready=s_axil_awready, 118 | m_axil_wdata=s_axil_wdata, 119 | m_axil_wstrb=s_axil_wstrb, 120 | m_axil_wvalid=s_axil_wvalid, 121 | m_axil_wready=s_axil_wready, 122 | m_axil_bresp=s_axil_bresp, 123 | m_axil_bvalid=s_axil_bvalid, 124 | m_axil_bready=s_axil_bready, 125 | m_axil_araddr=s_axil_araddr, 126 | m_axil_arprot=s_axil_arprot, 127 | m_axil_arvalid=s_axil_arvalid, 128 | m_axil_arready=s_axil_arready, 129 | m_axil_rdata=s_axil_rdata, 130 | m_axil_rresp=s_axil_rresp, 131 | m_axil_rvalid=s_axil_rvalid, 132 | m_axil_rready=s_axil_rready, 133 | pause=axil_master_pause, 134 | name='master' 135 | ) 136 | 137 | # I2C memory model 1 138 | i2c_mem_inst1 = i2c.I2CMem(1024) 139 | 140 | i2c_mem_logic1 = i2c_mem_inst1.create_logic( 141 | scl_i=s1_scl_i, 142 | scl_o=s1_scl_o, 143 | scl_t=s1_scl_t, 144 | sda_i=s1_sda_i, 145 | sda_o=s1_sda_o, 146 | sda_t=s1_sda_t, 147 | abw=2, 148 | address=0x50, 149 | latency=0, 150 | name='slave1' 151 | ) 152 | 153 | # I2C memory model 2 154 | i2c_mem_inst2 = i2c.I2CMem(1024) 155 | 156 | i2c_mem_logic2 = i2c_mem_inst2.create_logic( 157 | scl_i=s2_scl_i, 158 | scl_o=s2_scl_o, 159 | scl_t=s2_scl_t, 160 | sda_i=s2_sda_i, 161 | sda_o=s2_sda_o, 162 | sda_t=s2_sda_t, 163 | abw=2, 164 | address=0x51, 165 | latency=1000, 166 | name='slave2' 167 | ) 168 | 169 | # DUT 170 | if os.system(build_cmd): 171 | raise Exception("Error running build command") 172 | 173 | dut = Cosimulation( 174 | "vvp -m myhdl %s.vvp -lxt2" % testbench, 175 | clk=clk, 176 | rst=rst, 177 | current_test=current_test, 178 | 179 | s_axil_awaddr=s_axil_awaddr, 180 | s_axil_awprot=s_axil_awprot, 181 | s_axil_awvalid=s_axil_awvalid, 182 | s_axil_awready=s_axil_awready, 183 | s_axil_wdata=s_axil_wdata, 184 | s_axil_wstrb=s_axil_wstrb, 185 | s_axil_wvalid=s_axil_wvalid, 186 | s_axil_wready=s_axil_wready, 187 | s_axil_bresp=s_axil_bresp, 188 | s_axil_bvalid=s_axil_bvalid, 189 | s_axil_bready=s_axil_bready, 190 | s_axil_araddr=s_axil_araddr, 191 | s_axil_arprot=s_axil_arprot, 192 | s_axil_arvalid=s_axil_arvalid, 193 | s_axil_arready=s_axil_arready, 194 | s_axil_rdata=s_axil_rdata, 195 | s_axil_rresp=s_axil_rresp, 196 | s_axil_rvalid=s_axil_rvalid, 197 | s_axil_rready=s_axil_rready, 198 | 199 | i2c_scl_i=i2c_scl_i, 200 | i2c_scl_o=i2c_scl_o, 201 | i2c_scl_t=i2c_scl_t, 202 | i2c_sda_i=i2c_sda_i, 203 | i2c_sda_o=i2c_sda_o, 204 | i2c_sda_t=i2c_sda_t 205 | ) 206 | 207 | @always_comb 208 | def bus(): 209 | # emulate I2C wired AND 210 | scl = i2c_scl_o & s1_scl_o & s2_scl_o; 211 | sda = i2c_sda_o & s1_sda_o & s2_sda_o; 212 | 213 | i2c_scl_i.next = scl; 214 | i2c_sda_i.next = sda; 215 | 216 | s1_scl_i.next = scl; 217 | s1_sda_i.next = sda; 218 | 219 | s2_scl_i.next = scl; 220 | s2_sda_i.next = sda; 221 | 222 | @always(delay(4)) 223 | def clkgen(): 224 | clk.next = not clk 225 | 226 | @instance 227 | def check(): 228 | yield delay(100) 229 | yield clk.posedge 230 | rst.next = 1 231 | yield clk.posedge 232 | rst.next = 0 233 | yield clk.posedge 234 | yield delay(100) 235 | yield clk.posedge 236 | 237 | # testbench stimulus 238 | 239 | yield clk.posedge 240 | print("test 1: write") 241 | current_test.next = 1 242 | 243 | axil_master_inst.init_write(4, b'\x50\x04\x00\x00\x00') 244 | axil_master_inst.init_write(4, b'\x50\x04\x00\x00\x04') 245 | axil_master_inst.init_write(4, b'\x50\x04\x00\x00\x11') 246 | axil_master_inst.init_write(4, b'\x50\x04\x00\x00\x22') 247 | axil_master_inst.init_write(4, b'\x50\x04\x00\x00\x33') 248 | axil_master_inst.init_write(4, b'\x50\x14\x00\x00\x44') 249 | 250 | yield axil_master_inst.wait() 251 | yield clk.posedge 252 | 253 | while True: 254 | axil_master_inst.init_read(0, 1) 255 | yield axil_master_inst.wait() 256 | data = axil_master_inst.get_read_data() 257 | if data[1][0] & 0x03 == 0: 258 | break 259 | 260 | data = i2c_mem_inst1.read_mem(0, 32) 261 | for i in range(0, len(data), 16): 262 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 263 | 264 | assert i2c_mem_inst1.read_mem(4,4) == b'\x11\x22\x33\x44' 265 | 266 | yield delay(100) 267 | 268 | yield clk.posedge 269 | print("test 2: read") 270 | current_test.next = 2 271 | 272 | axil_master_inst.init_write(4, b'\x50\x04\x00\x00\x00') 273 | axil_master_inst.init_write(4, b'\x50\x04\x00\x00\x04') 274 | axil_master_inst.init_write(4, b'\x50\x03') 275 | axil_master_inst.init_write(4, b'\x50\x02') 276 | axil_master_inst.init_write(4, b'\x50\x02') 277 | axil_master_inst.init_write(4, b'\x50\x12') 278 | 279 | yield axil_master_inst.wait() 280 | yield clk.posedge 281 | 282 | while True: 283 | axil_master_inst.init_read(0, 1) 284 | yield axil_master_inst.wait() 285 | data = axil_master_inst.get_read_data() 286 | if data[1][0] & 0x03 == 0: 287 | break 288 | 289 | axil_master_inst.init_read(8, 2) 290 | axil_master_inst.init_read(8, 2) 291 | axil_master_inst.init_read(8, 2) 292 | axil_master_inst.init_read(8, 2) 293 | 294 | yield axil_master_inst.wait() 295 | yield clk.posedge 296 | 297 | data = axil_master_inst.get_read_data() 298 | assert data[1] == b'\x11\x01' 299 | 300 | data = axil_master_inst.get_read_data() 301 | assert data[1] == b'\x22\x01' 302 | 303 | data = axil_master_inst.get_read_data() 304 | assert data[1] == b'\x33\x01' 305 | 306 | data = axil_master_inst.get_read_data() 307 | assert data[1] == b'\x44\x03' 308 | 309 | yield delay(100) 310 | 311 | yield clk.posedge 312 | print("test 3: write to slave 2") 313 | current_test.next = 3 314 | 315 | axil_master_inst.init_write(4, b'\x51\x08\x00\x00\x00\x00') 316 | axil_master_inst.init_write(8, b'\x04\x00') 317 | axil_master_inst.init_write(8, b'\x44\x00') 318 | axil_master_inst.init_write(8, b'\x33\x00') 319 | axil_master_inst.init_write(8, b'\x22\x00') 320 | axil_master_inst.init_write(8, b'\x11\x02') 321 | axil_master_inst.init_write(4, b'\x51\x10') 322 | 323 | yield axil_master_inst.wait() 324 | yield clk.posedge 325 | 326 | while True: 327 | axil_master_inst.init_read(0, 1) 328 | yield axil_master_inst.wait() 329 | data = axil_master_inst.get_read_data() 330 | if data[1][0] & 0x03 == 0: 331 | break 332 | 333 | data = i2c_mem_inst2.read_mem(0, 32) 334 | for i in range(0, len(data), 16): 335 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 336 | 337 | assert i2c_mem_inst2.read_mem(4,4) == b'\x44\x33\x22\x11' 338 | 339 | yield delay(100) 340 | 341 | yield clk.posedge 342 | print("test 4: read from slave 2") 343 | current_test.next = 4 344 | 345 | axil_master_inst.init_write(4, b'\x51\x04\x00\x00\x00') 346 | axil_master_inst.init_write(4, b'\x51\x04\x00\x00\x04') 347 | axil_master_inst.init_write(4, b'\x51\x03') 348 | axil_master_inst.init_write(4, b'\x51\x02') 349 | axil_master_inst.init_write(4, b'\x51\x02') 350 | axil_master_inst.init_write(4, b'\x51\x12') 351 | 352 | yield axil_master_inst.wait() 353 | yield clk.posedge 354 | 355 | while True: 356 | axil_master_inst.init_read(0, 1) 357 | yield axil_master_inst.wait() 358 | data = axil_master_inst.get_read_data() 359 | if data[1][0] & 0x03 == 0: 360 | break 361 | 362 | axil_master_inst.init_read(8, 2) 363 | axil_master_inst.init_read(8, 2) 364 | axil_master_inst.init_read(8, 2) 365 | axil_master_inst.init_read(8, 2) 366 | 367 | yield axil_master_inst.wait() 368 | yield clk.posedge 369 | 370 | data = axil_master_inst.get_read_data() 371 | assert data[1] == b'\x44\x01' 372 | 373 | data = axil_master_inst.get_read_data() 374 | assert data[1] == b'\x33\x01' 375 | 376 | data = axil_master_inst.get_read_data() 377 | assert data[1] == b'\x22\x01' 378 | 379 | data = axil_master_inst.get_read_data() 380 | assert data[1] == b'\x11\x03' 381 | 382 | yield delay(100) 383 | 384 | yield clk.posedge 385 | print("test 5: write to nonexistent device") 386 | current_test.next = 5 387 | 388 | axil_master_inst.init_write(4, b'\x52\x04\x00\x00\x00') 389 | axil_master_inst.init_write(4, b'\x52\x04\x00\x00\x04') 390 | axil_master_inst.init_write(4, b'\x52\x04\x00\x00\xde') 391 | axil_master_inst.init_write(4, b'\x52\x04\x00\x00\xad') 392 | axil_master_inst.init_write(4, b'\x52\x04\x00\x00\xbe') 393 | axil_master_inst.init_write(4, b'\x52\x14\x00\x00\xef') 394 | 395 | yield axil_master_inst.wait() 396 | yield clk.posedge 397 | 398 | got_missed_ack = False 399 | 400 | while True: 401 | axil_master_inst.init_read(0, 1) 402 | yield axil_master_inst.wait() 403 | data = axil_master_inst.get_read_data() 404 | if data[1][0] & 0x08: 405 | got_missed_ack = True 406 | if data[1][0] & 0x03 == 0: 407 | break 408 | 409 | assert got_missed_ack 410 | 411 | yield delay(100) 412 | 413 | raise StopSimulation 414 | 415 | return instances() 416 | 417 | def test_bench(): 418 | sim = Simulation(bench()) 419 | sim.run() 420 | 421 | if __name__ == '__main__': 422 | print("Running test...") 423 | test_bench() 424 | -------------------------------------------------------------------------------- /tb/test_i2c_master_axil.v: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2019 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | // Language: Verilog 2001 26 | 27 | `timescale 1ns / 1ps 28 | 29 | /* 30 | * Testbench for i2c_master_axil 31 | */ 32 | module test_i2c_master_axil; 33 | 34 | // Parameters 35 | parameter DEFAULT_PRESCALE = 1; 36 | parameter FIXED_PRESCALE = 0; 37 | parameter CMD_FIFO = 1; 38 | parameter CMD_FIFO_DEPTH = 32; 39 | parameter WRITE_FIFO = 1; 40 | parameter WRITE_FIFO_DEPTH = 32; 41 | parameter READ_FIFO = 1; 42 | parameter READ_FIFO_DEPTH = 32; 43 | 44 | // Inputs 45 | reg clk = 0; 46 | reg rst = 0; 47 | reg [7:0] current_test = 0; 48 | 49 | reg [3:0] s_axil_awaddr = 0; 50 | reg [2:0] s_axil_awprot = 0; 51 | reg s_axil_awvalid = 0; 52 | reg [31:0] s_axil_wdata = 0; 53 | reg [3:0] s_axil_wstrb = 0; 54 | reg s_axil_wvalid = 0; 55 | reg s_axil_bready = 0; 56 | reg [3:0] s_axil_araddr = 0; 57 | reg [2:0] s_axil_arprot = 0; 58 | reg s_axil_arvalid = 0; 59 | reg s_axil_rready = 0; 60 | reg i2c_scl_i = 1; 61 | reg i2c_sda_i = 1; 62 | 63 | // Outputs 64 | wire s_axil_awready; 65 | wire s_axil_wready; 66 | wire [1:0] s_axil_bresp; 67 | wire s_axil_bvalid; 68 | wire s_axil_arready; 69 | wire [31:0] s_axil_rdata; 70 | wire [1:0] s_axil_rresp; 71 | wire s_axil_rvalid; 72 | wire i2c_scl_o; 73 | wire i2c_scl_t; 74 | wire i2c_sda_o; 75 | wire i2c_sda_t; 76 | 77 | initial begin 78 | // myhdl integration 79 | $from_myhdl( 80 | clk, 81 | rst, 82 | current_test, 83 | s_axil_awaddr, 84 | s_axil_awprot, 85 | s_axil_awvalid, 86 | s_axil_wdata, 87 | s_axil_wstrb, 88 | s_axil_wvalid, 89 | s_axil_bready, 90 | s_axil_araddr, 91 | s_axil_arprot, 92 | s_axil_arvalid, 93 | s_axil_rready, 94 | i2c_scl_i, 95 | i2c_sda_i 96 | ); 97 | $to_myhdl( 98 | s_axil_awready, 99 | s_axil_wready, 100 | s_axil_bresp, 101 | s_axil_bvalid, 102 | s_axil_arready, 103 | s_axil_rdata, 104 | s_axil_rresp, 105 | s_axil_rvalid, 106 | i2c_scl_o, 107 | i2c_scl_t, 108 | i2c_sda_o, 109 | i2c_sda_t 110 | ); 111 | 112 | // dump file 113 | $dumpfile("test_i2c_master_axil.lxt"); 114 | $dumpvars(0, test_i2c_master_axil); 115 | end 116 | 117 | i2c_master_axil #( 118 | .DEFAULT_PRESCALE(DEFAULT_PRESCALE), 119 | .FIXED_PRESCALE(FIXED_PRESCALE), 120 | .CMD_FIFO(CMD_FIFO), 121 | .CMD_FIFO_DEPTH(CMD_FIFO_DEPTH), 122 | .WRITE_FIFO(WRITE_FIFO), 123 | .WRITE_FIFO_DEPTH(WRITE_FIFO_DEPTH), 124 | .READ_FIFO(READ_FIFO), 125 | .READ_FIFO_DEPTH(READ_FIFO_DEPTH) 126 | ) 127 | UUT ( 128 | .clk(clk), 129 | .rst(rst), 130 | .s_axil_awaddr(s_axil_awaddr), 131 | .s_axil_awprot(s_axil_awprot), 132 | .s_axil_awvalid(s_axil_awvalid), 133 | .s_axil_awready(s_axil_awready), 134 | .s_axil_wdata(s_axil_wdata), 135 | .s_axil_wstrb(s_axil_wstrb), 136 | .s_axil_wvalid(s_axil_wvalid), 137 | .s_axil_wready(s_axil_wready), 138 | .s_axil_bresp(s_axil_bresp), 139 | .s_axil_bvalid(s_axil_bvalid), 140 | .s_axil_bready(s_axil_bready), 141 | .s_axil_araddr(s_axil_araddr), 142 | .s_axil_arprot(s_axil_arprot), 143 | .s_axil_arvalid(s_axil_arvalid), 144 | .s_axil_arready(s_axil_arready), 145 | .s_axil_rdata(s_axil_rdata), 146 | .s_axil_rresp(s_axil_rresp), 147 | .s_axil_rvalid(s_axil_rvalid), 148 | .s_axil_rready(s_axil_rready), 149 | .i2c_scl_i(i2c_scl_i), 150 | .i2c_scl_o(i2c_scl_o), 151 | .i2c_scl_t(i2c_scl_t), 152 | .i2c_sda_i(i2c_sda_i), 153 | .i2c_sda_o(i2c_sda_o), 154 | .i2c_sda_t(i2c_sda_t) 155 | ); 156 | 157 | endmodule 158 | -------------------------------------------------------------------------------- /tb/test_i2c_master_wbs_16.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | Copyright (c) 2017 Alex Forencich 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | """ 25 | 26 | from myhdl import * 27 | import os 28 | 29 | import i2c 30 | import wb 31 | 32 | module = 'i2c_master_wbs_16' 33 | testbench = 'test_%s' % module 34 | 35 | srcs = [] 36 | 37 | srcs.append("../rtl/%s.v" % module) 38 | srcs.append("../rtl/i2c_master.v") 39 | srcs.append("../rtl/axis_fifo.v") 40 | srcs.append("%s.v" % testbench) 41 | 42 | src = ' '.join(srcs) 43 | 44 | build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) 45 | 46 | def bench(): 47 | 48 | # Parameters 49 | DEFAULT_PRESCALE = 1 50 | FIXED_PRESCALE = 0 51 | CMD_FIFO = 1 52 | CMD_FIFO_DEPTH = 32 53 | WRITE_FIFO = 1 54 | WRITE_FIFO_DEPTH = 32 55 | READ_FIFO = 1 56 | READ_FIFO_DEPTH = 32 57 | 58 | # Inputs 59 | clk = Signal(bool(0)) 60 | rst = Signal(bool(0)) 61 | current_test = Signal(intbv(0)[8:]) 62 | 63 | wbs_adr_i = Signal(intbv(0)[3:]) 64 | wbs_dat_i = Signal(intbv(0)[16:]) 65 | wbs_we_i = Signal(bool(0)) 66 | wbs_sel_i = Signal(intbv(0)[2:]) 67 | wbs_stb_i = Signal(bool(0)) 68 | wbs_cyc_i = Signal(bool(0)) 69 | i2c_scl_i = Signal(bool(1)) 70 | i2c_sda_i = Signal(bool(1)) 71 | 72 | s1_scl_i = Signal(bool(1)) 73 | s1_sda_i = Signal(bool(1)) 74 | 75 | s2_scl_i = Signal(bool(1)) 76 | s2_sda_i = Signal(bool(1)) 77 | 78 | # Outputs 79 | wbs_dat_o = Signal(intbv(0)[16:]) 80 | wbs_ack_o = Signal(bool(0)) 81 | i2c_scl_o = Signal(bool(1)) 82 | i2c_scl_t = Signal(bool(1)) 83 | i2c_sda_o = Signal(bool(1)) 84 | i2c_sda_t = Signal(bool(1)) 85 | 86 | s1_scl_o = Signal(bool(1)) 87 | s1_scl_t = Signal(bool(1)) 88 | s1_sda_o = Signal(bool(1)) 89 | s1_sda_t = Signal(bool(1)) 90 | 91 | s2_scl_o = Signal(bool(1)) 92 | s2_scl_t = Signal(bool(1)) 93 | s2_sda_o = Signal(bool(1)) 94 | s2_sda_t = Signal(bool(1)) 95 | 96 | # WB master 97 | wbm_inst = wb.WBMaster() 98 | 99 | wbm_logic = wbm_inst.create_logic( 100 | clk, 101 | adr_o=wbs_adr_i, 102 | dat_i=wbs_dat_o, 103 | dat_o=wbs_dat_i, 104 | we_o=wbs_we_i, 105 | sel_o=wbs_sel_i, 106 | stb_o=wbs_stb_i, 107 | ack_i=wbs_ack_o, 108 | cyc_o=wbs_cyc_i, 109 | name='master' 110 | ) 111 | 112 | # I2C memory model 1 113 | i2c_mem_inst1 = i2c.I2CMem(1024) 114 | 115 | i2c_mem_logic1 = i2c_mem_inst1.create_logic( 116 | scl_i=s1_scl_i, 117 | scl_o=s1_scl_o, 118 | scl_t=s1_scl_t, 119 | sda_i=s1_sda_i, 120 | sda_o=s1_sda_o, 121 | sda_t=s1_sda_t, 122 | abw=2, 123 | address=0x50, 124 | latency=0, 125 | name='slave1' 126 | ) 127 | 128 | # I2C memory model 2 129 | i2c_mem_inst2 = i2c.I2CMem(1024) 130 | 131 | i2c_mem_logic2 = i2c_mem_inst2.create_logic( 132 | scl_i=s2_scl_i, 133 | scl_o=s2_scl_o, 134 | scl_t=s2_scl_t, 135 | sda_i=s2_sda_i, 136 | sda_o=s2_sda_o, 137 | sda_t=s2_sda_t, 138 | abw=2, 139 | address=0x51, 140 | latency=1000, 141 | name='slave2' 142 | ) 143 | 144 | # DUT 145 | if os.system(build_cmd): 146 | raise Exception("Error running build command") 147 | 148 | dut = Cosimulation( 149 | "vvp -m myhdl %s.vvp -lxt2" % testbench, 150 | clk=clk, 151 | rst=rst, 152 | current_test=current_test, 153 | wbs_adr_i=wbs_adr_i, 154 | wbs_dat_i=wbs_dat_i, 155 | wbs_dat_o=wbs_dat_o, 156 | wbs_we_i=wbs_we_i, 157 | wbs_sel_i=wbs_sel_i, 158 | wbs_stb_i=wbs_stb_i, 159 | wbs_ack_o=wbs_ack_o, 160 | wbs_cyc_i=wbs_cyc_i, 161 | i2c_scl_i=i2c_scl_i, 162 | i2c_scl_o=i2c_scl_o, 163 | i2c_scl_t=i2c_scl_t, 164 | i2c_sda_i=i2c_sda_i, 165 | i2c_sda_o=i2c_sda_o, 166 | i2c_sda_t=i2c_sda_t 167 | ) 168 | 169 | @always_comb 170 | def bus(): 171 | # emulate I2C wired AND 172 | i2c_scl_i.next = i2c_scl_o & s1_scl_o & s2_scl_o; 173 | i2c_sda_i.next = i2c_sda_o & s1_sda_o & s2_sda_o; 174 | 175 | s1_scl_i.next = i2c_scl_o & s1_scl_o & s2_scl_o; 176 | s1_sda_i.next = i2c_sda_o & s1_sda_o & s2_sda_o; 177 | 178 | s2_scl_i.next = i2c_scl_o & s1_scl_o & s2_scl_o; 179 | s2_sda_i.next = i2c_sda_o & s1_sda_o & s2_sda_o; 180 | 181 | @always(delay(4)) 182 | def clkgen(): 183 | clk.next = not clk 184 | 185 | @instance 186 | def check(): 187 | yield delay(100) 188 | yield clk.posedge 189 | rst.next = 1 190 | yield clk.posedge 191 | rst.next = 0 192 | yield clk.posedge 193 | yield delay(100) 194 | yield clk.posedge 195 | 196 | # testbench stimulus 197 | 198 | yield clk.posedge 199 | print("test 1: write") 200 | current_test.next = 1 201 | 202 | wbm_inst.init_write(2, b'\x50\x04\x00') 203 | wbm_inst.init_write(3, b'\x04\x04') 204 | wbm_inst.init_write(3, b'\x04\x11') 205 | wbm_inst.init_write(3, b'\x04\x22') 206 | wbm_inst.init_write(3, b'\x04\x33') 207 | wbm_inst.init_write(3, b'\x14\x44') 208 | 209 | yield wbm_inst.wait() 210 | yield clk.posedge 211 | 212 | while True: 213 | wbm_inst.init_read(0, 1) 214 | yield wbm_inst.wait() 215 | data = wbm_inst.get_read_data() 216 | if data[1][0] & 0x03 == 0: 217 | break 218 | 219 | data = i2c_mem_inst1.read_mem(0, 32) 220 | for i in range(0, len(data), 16): 221 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 222 | 223 | assert i2c_mem_inst1.read_mem(4,4) == b'\x11\x22\x33\x44' 224 | 225 | yield delay(100) 226 | 227 | yield clk.posedge 228 | print("test 2: read") 229 | current_test.next = 2 230 | 231 | wbm_inst.init_write(2, b'\x50\x04\x00') 232 | wbm_inst.init_write(3, b'\x04\x04') 233 | wbm_inst.init_write(3, b'\x03') 234 | wbm_inst.init_write(3, b'\x02') 235 | wbm_inst.init_write(3, b'\x02') 236 | wbm_inst.init_write(3, b'\x12') 237 | 238 | yield wbm_inst.wait() 239 | yield clk.posedge 240 | 241 | while True: 242 | wbm_inst.init_read(0, 1) 243 | yield wbm_inst.wait() 244 | data = wbm_inst.get_read_data() 245 | if data[1][0] & 0x03 == 0: 246 | break 247 | 248 | wbm_inst.init_read(4, 2) 249 | wbm_inst.init_read(4, 2) 250 | wbm_inst.init_read(4, 2) 251 | wbm_inst.init_read(4, 2) 252 | 253 | yield wbm_inst.wait() 254 | yield clk.posedge 255 | 256 | data = wbm_inst.get_read_data() 257 | assert data[1] == b'\x11\x01' 258 | 259 | data = wbm_inst.get_read_data() 260 | assert data[1] == b'\x22\x01' 261 | 262 | data = wbm_inst.get_read_data() 263 | assert data[1] == b'\x33\x01' 264 | 265 | data = wbm_inst.get_read_data() 266 | assert data[1] == b'\x44\x03' 267 | 268 | yield delay(100) 269 | 270 | yield clk.posedge 271 | print("test 3: write to slave 2") 272 | current_test.next = 3 273 | 274 | wbm_inst.init_write(2, b'\x51\x08\x00\x00') 275 | wbm_inst.init_write(4, b'\x04\x00') 276 | wbm_inst.init_write(4, b'\x44\x00') 277 | wbm_inst.init_write(4, b'\x33\x00') 278 | wbm_inst.init_write(4, b'\x22\x00') 279 | wbm_inst.init_write(4, b'\x11\x02') 280 | wbm_inst.init_write(3, b'\x10') 281 | 282 | yield wbm_inst.wait() 283 | yield clk.posedge 284 | 285 | while True: 286 | wbm_inst.init_read(0, 1) 287 | yield wbm_inst.wait() 288 | data = wbm_inst.get_read_data() 289 | if data[1][0] & 0x03 == 0: 290 | break 291 | 292 | data = i2c_mem_inst2.read_mem(0, 32) 293 | for i in range(0, len(data), 16): 294 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 295 | 296 | assert i2c_mem_inst2.read_mem(4,4) == b'\x44\x33\x22\x11' 297 | 298 | yield delay(100) 299 | 300 | yield clk.posedge 301 | print("test 4: read from slave 2") 302 | current_test.next = 4 303 | 304 | wbm_inst.init_write(2, b'\x51\x04\x00') 305 | wbm_inst.init_write(3, b'\x04\x04') 306 | wbm_inst.init_write(3, b'\x03') 307 | wbm_inst.init_write(3, b'\x02') 308 | wbm_inst.init_write(3, b'\x02') 309 | wbm_inst.init_write(3, b'\x12') 310 | 311 | yield wbm_inst.wait() 312 | yield clk.posedge 313 | 314 | while True: 315 | wbm_inst.init_read(0, 1) 316 | yield wbm_inst.wait() 317 | data = wbm_inst.get_read_data() 318 | if data[1][0] & 0x03 == 0: 319 | break 320 | 321 | wbm_inst.init_read(4, 2) 322 | wbm_inst.init_read(4, 2) 323 | wbm_inst.init_read(4, 2) 324 | wbm_inst.init_read(4, 2) 325 | 326 | yield wbm_inst.wait() 327 | yield clk.posedge 328 | 329 | data = wbm_inst.get_read_data() 330 | assert data[1] == b'\x44\x01' 331 | 332 | data = wbm_inst.get_read_data() 333 | assert data[1] == b'\x33\x01' 334 | 335 | data = wbm_inst.get_read_data() 336 | assert data[1] == b'\x22\x01' 337 | 338 | data = wbm_inst.get_read_data() 339 | assert data[1] == b'\x11\x03' 340 | 341 | yield delay(100) 342 | 343 | yield clk.posedge 344 | print("test 5: write to nonexistent device") 345 | current_test.next = 5 346 | 347 | wbm_inst.init_write(2, b'\x52\x04\x00') 348 | wbm_inst.init_write(3, b'\x04\x04') 349 | wbm_inst.init_write(3, b'\x04\xde') 350 | wbm_inst.init_write(3, b'\x04\xad') 351 | wbm_inst.init_write(3, b'\x04\xbe') 352 | wbm_inst.init_write(3, b'\x14\xef') 353 | 354 | yield wbm_inst.wait() 355 | yield clk.posedge 356 | 357 | got_missed_ack = False 358 | 359 | while True: 360 | wbm_inst.init_read(0, 1) 361 | yield wbm_inst.wait() 362 | data = wbm_inst.get_read_data() 363 | if data[1][0] & 0x08: 364 | got_missed_ack = True 365 | if data[1][0] & 0x03 == 0: 366 | break 367 | 368 | assert got_missed_ack 369 | 370 | yield delay(100) 371 | 372 | raise StopSimulation 373 | 374 | return instances() 375 | 376 | def test_bench(): 377 | sim = Simulation(bench()) 378 | sim.run() 379 | 380 | if __name__ == '__main__': 381 | print("Running test...") 382 | test_bench() 383 | -------------------------------------------------------------------------------- /tb/test_i2c_master_wbs_16.v: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2017 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | // Language: Verilog 2001 26 | 27 | `timescale 1ns / 1ps 28 | 29 | /* 30 | * Testbench for i2c_master_wbs_16 31 | */ 32 | module test_i2c_master_wbs_16; 33 | 34 | // Parameters 35 | parameter DEFAULT_PRESCALE = 1; 36 | parameter FIXED_PRESCALE = 0; 37 | parameter CMD_FIFO = 1; 38 | parameter CMD_FIFO_DEPTH = 32; 39 | parameter WRITE_FIFO = 1; 40 | parameter WRITE_FIFO_DEPTH = 32; 41 | parameter READ_FIFO = 1; 42 | parameter READ_FIFO_DEPTH = 32; 43 | 44 | // Inputs 45 | reg clk = 0; 46 | reg rst = 0; 47 | reg [7:0] current_test = 0; 48 | 49 | reg [2:0] wbs_adr_i = 0; 50 | reg [15:0] wbs_dat_i = 0; 51 | reg wbs_we_i = 0; 52 | reg [1:0] wbs_sel_i = 0; 53 | reg wbs_stb_i = 0; 54 | reg wbs_cyc_i = 0; 55 | reg i2c_scl_i = 1; 56 | reg i2c_sda_i = 1; 57 | 58 | // Outputs 59 | wire [15:0] wbs_dat_o; 60 | wire wbs_ack_o; 61 | wire i2c_scl_o; 62 | wire i2c_scl_t; 63 | wire i2c_sda_o; 64 | wire i2c_sda_t; 65 | 66 | initial begin 67 | // myhdl integration 68 | $from_myhdl( 69 | clk, 70 | rst, 71 | current_test, 72 | wbs_adr_i, 73 | wbs_dat_i, 74 | wbs_we_i, 75 | wbs_sel_i, 76 | wbs_stb_i, 77 | wbs_cyc_i, 78 | i2c_scl_i, 79 | i2c_sda_i 80 | ); 81 | $to_myhdl( 82 | wbs_dat_o, 83 | wbs_ack_o, 84 | i2c_scl_o, 85 | i2c_scl_t, 86 | i2c_sda_o, 87 | i2c_sda_t 88 | ); 89 | 90 | // dump file 91 | $dumpfile("test_i2c_master_wbs_16.lxt"); 92 | $dumpvars(0, test_i2c_master_wbs_16); 93 | end 94 | 95 | i2c_master_wbs_16 #( 96 | .DEFAULT_PRESCALE(DEFAULT_PRESCALE), 97 | .FIXED_PRESCALE(FIXED_PRESCALE), 98 | .CMD_FIFO(CMD_FIFO), 99 | .CMD_FIFO_DEPTH(CMD_FIFO_DEPTH), 100 | .WRITE_FIFO(WRITE_FIFO), 101 | .WRITE_FIFO_DEPTH(WRITE_FIFO_DEPTH), 102 | .READ_FIFO(READ_FIFO), 103 | .READ_FIFO_DEPTH(READ_FIFO_DEPTH) 104 | ) 105 | UUT ( 106 | .clk(clk), 107 | .rst(rst), 108 | .wbs_adr_i(wbs_adr_i), 109 | .wbs_dat_i(wbs_dat_i), 110 | .wbs_dat_o(wbs_dat_o), 111 | .wbs_we_i(wbs_we_i), 112 | .wbs_sel_i(wbs_sel_i), 113 | .wbs_stb_i(wbs_stb_i), 114 | .wbs_ack_o(wbs_ack_o), 115 | .wbs_cyc_i(wbs_cyc_i), 116 | .i2c_scl_i(i2c_scl_i), 117 | .i2c_scl_o(i2c_scl_o), 118 | .i2c_scl_t(i2c_scl_t), 119 | .i2c_sda_i(i2c_sda_i), 120 | .i2c_sda_o(i2c_sda_o), 121 | .i2c_sda_t(i2c_sda_t) 122 | ); 123 | 124 | endmodule 125 | -------------------------------------------------------------------------------- /tb/test_i2c_master_wbs_8.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | Copyright (c) 2016-2017 Alex Forencich 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | """ 25 | 26 | from myhdl import * 27 | import os 28 | 29 | import i2c 30 | import wb 31 | 32 | module = 'i2c_master_wbs_8' 33 | testbench = 'test_%s' % module 34 | 35 | srcs = [] 36 | 37 | srcs.append("../rtl/%s.v" % module) 38 | srcs.append("../rtl/i2c_master.v") 39 | srcs.append("../rtl/axis_fifo.v") 40 | srcs.append("%s.v" % testbench) 41 | 42 | src = ' '.join(srcs) 43 | 44 | build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) 45 | 46 | def bench(): 47 | 48 | # Parameters 49 | DEFAULT_PRESCALE = 1 50 | FIXED_PRESCALE = 0 51 | CMD_FIFO = 1 52 | CMD_FIFO_DEPTH = 32 53 | WRITE_FIFO = 1 54 | WRITE_FIFO_DEPTH = 32 55 | READ_FIFO = 1 56 | READ_FIFO_DEPTH = 32 57 | 58 | # Inputs 59 | clk = Signal(bool(0)) 60 | rst = Signal(bool(0)) 61 | current_test = Signal(intbv(0)[8:]) 62 | 63 | wbs_adr_i = Signal(intbv(0)[3:]) 64 | wbs_dat_i = Signal(intbv(0)[8:]) 65 | wbs_we_i = Signal(bool(0)) 66 | wbs_stb_i = Signal(bool(0)) 67 | wbs_cyc_i = Signal(bool(0)) 68 | i2c_scl_i = Signal(bool(1)) 69 | i2c_sda_i = Signal(bool(1)) 70 | 71 | s1_scl_i = Signal(bool(1)) 72 | s1_sda_i = Signal(bool(1)) 73 | 74 | s2_scl_i = Signal(bool(1)) 75 | s2_sda_i = Signal(bool(1)) 76 | 77 | # Outputs 78 | wbs_dat_o = Signal(intbv(0)[8:]) 79 | wbs_ack_o = Signal(bool(0)) 80 | i2c_scl_o = Signal(bool(1)) 81 | i2c_scl_t = Signal(bool(1)) 82 | i2c_sda_o = Signal(bool(1)) 83 | i2c_sda_t = Signal(bool(1)) 84 | 85 | s1_scl_o = Signal(bool(1)) 86 | s1_scl_t = Signal(bool(1)) 87 | s1_sda_o = Signal(bool(1)) 88 | s1_sda_t = Signal(bool(1)) 89 | 90 | s2_scl_o = Signal(bool(1)) 91 | s2_scl_t = Signal(bool(1)) 92 | s2_sda_o = Signal(bool(1)) 93 | s2_sda_t = Signal(bool(1)) 94 | 95 | # WB master 96 | wbm_inst = wb.WBMaster() 97 | 98 | wbm_logic = wbm_inst.create_logic( 99 | clk, 100 | adr_o=wbs_adr_i, 101 | dat_i=wbs_dat_o, 102 | dat_o=wbs_dat_i, 103 | we_o=wbs_we_i, 104 | stb_o=wbs_stb_i, 105 | ack_i=wbs_ack_o, 106 | cyc_o=wbs_cyc_i, 107 | name='master' 108 | ) 109 | 110 | # I2C memory model 1 111 | i2c_mem_inst1 = i2c.I2CMem(1024) 112 | 113 | i2c_mem_logic1 = i2c_mem_inst1.create_logic( 114 | scl_i=s1_scl_i, 115 | scl_o=s1_scl_o, 116 | scl_t=s1_scl_t, 117 | sda_i=s1_sda_i, 118 | sda_o=s1_sda_o, 119 | sda_t=s1_sda_t, 120 | abw=2, 121 | address=0x50, 122 | latency=0, 123 | name='slave1' 124 | ) 125 | 126 | # I2C memory model 2 127 | i2c_mem_inst2 = i2c.I2CMem(1024) 128 | 129 | i2c_mem_logic2 = i2c_mem_inst2.create_logic( 130 | scl_i=s2_scl_i, 131 | scl_o=s2_scl_o, 132 | scl_t=s2_scl_t, 133 | sda_i=s2_sda_i, 134 | sda_o=s2_sda_o, 135 | sda_t=s2_sda_t, 136 | abw=2, 137 | address=0x51, 138 | latency=1000, 139 | name='slave2' 140 | ) 141 | 142 | # DUT 143 | if os.system(build_cmd): 144 | raise Exception("Error running build command") 145 | 146 | dut = Cosimulation( 147 | "vvp -m myhdl %s.vvp -lxt2" % testbench, 148 | clk=clk, 149 | rst=rst, 150 | current_test=current_test, 151 | 152 | wbs_adr_i=wbs_adr_i, 153 | wbs_dat_i=wbs_dat_i, 154 | wbs_dat_o=wbs_dat_o, 155 | wbs_we_i=wbs_we_i, 156 | wbs_stb_i=wbs_stb_i, 157 | wbs_ack_o=wbs_ack_o, 158 | wbs_cyc_i=wbs_cyc_i, 159 | 160 | i2c_scl_i=i2c_scl_i, 161 | i2c_scl_o=i2c_scl_o, 162 | i2c_scl_t=i2c_scl_t, 163 | i2c_sda_i=i2c_sda_i, 164 | i2c_sda_o=i2c_sda_o, 165 | i2c_sda_t=i2c_sda_t 166 | ) 167 | 168 | @always_comb 169 | def bus(): 170 | # emulate I2C wired AND 171 | i2c_scl_i.next = i2c_scl_o & s1_scl_o & s2_scl_o; 172 | i2c_sda_i.next = i2c_sda_o & s1_sda_o & s2_sda_o; 173 | 174 | s1_scl_i.next = i2c_scl_o & s1_scl_o & s2_scl_o; 175 | s1_sda_i.next = i2c_sda_o & s1_sda_o & s2_sda_o; 176 | 177 | s2_scl_i.next = i2c_scl_o & s1_scl_o & s2_scl_o; 178 | s2_sda_i.next = i2c_sda_o & s1_sda_o & s2_sda_o; 179 | 180 | @always(delay(4)) 181 | def clkgen(): 182 | clk.next = not clk 183 | 184 | @instance 185 | def check(): 186 | yield delay(100) 187 | yield clk.posedge 188 | rst.next = 1 189 | yield clk.posedge 190 | rst.next = 0 191 | yield clk.posedge 192 | yield delay(100) 193 | yield clk.posedge 194 | 195 | # testbench stimulus 196 | 197 | yield clk.posedge 198 | print("test 1: write") 199 | current_test.next = 1 200 | 201 | wbm_inst.init_write(2, b'\x50\x04\x00') 202 | wbm_inst.init_write(3, b'\x04\x04') 203 | wbm_inst.init_write(3, b'\x04\x11') 204 | wbm_inst.init_write(3, b'\x04\x22') 205 | wbm_inst.init_write(3, b'\x04\x33') 206 | wbm_inst.init_write(3, b'\x14\x44') 207 | 208 | yield wbm_inst.wait() 209 | yield clk.posedge 210 | 211 | while True: 212 | wbm_inst.init_read(0, 1) 213 | yield wbm_inst.wait() 214 | data = wbm_inst.get_read_data() 215 | if data[1][0] & 0x03 == 0: 216 | break 217 | 218 | data = i2c_mem_inst1.read_mem(0, 32) 219 | for i in range(0, len(data), 16): 220 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 221 | 222 | assert i2c_mem_inst1.read_mem(4,4) == b'\x11\x22\x33\x44' 223 | 224 | yield delay(100) 225 | 226 | yield clk.posedge 227 | print("test 2: read") 228 | current_test.next = 2 229 | 230 | wbm_inst.init_write(2, b'\x50\x04\x00') 231 | wbm_inst.init_write(3, b'\x04\x04') 232 | wbm_inst.init_write(3, b'\x03') 233 | wbm_inst.init_write(3, b'\x02') 234 | wbm_inst.init_write(3, b'\x02') 235 | wbm_inst.init_write(3, b'\x12') 236 | 237 | yield wbm_inst.wait() 238 | yield clk.posedge 239 | 240 | while True: 241 | wbm_inst.init_read(0, 1) 242 | yield wbm_inst.wait() 243 | data = wbm_inst.get_read_data() 244 | if data[1][0] & 0x03 == 0: 245 | break 246 | 247 | wbm_inst.init_read(4, 1) 248 | wbm_inst.init_read(4, 1) 249 | wbm_inst.init_read(4, 1) 250 | wbm_inst.init_read(4, 1) 251 | 252 | yield wbm_inst.wait() 253 | yield clk.posedge 254 | 255 | data = wbm_inst.get_read_data() 256 | assert data[1] == b'\x11' 257 | 258 | data = wbm_inst.get_read_data() 259 | assert data[1] == b'\x22' 260 | 261 | data = wbm_inst.get_read_data() 262 | assert data[1] == b'\x33' 263 | 264 | data = wbm_inst.get_read_data() 265 | assert data[1] == b'\x44' 266 | 267 | yield delay(100) 268 | 269 | yield clk.posedge 270 | print("test 3: write to slave 2") 271 | current_test.next = 3 272 | 273 | wbm_inst.init_write(2, b'\x51\x04\x00') 274 | wbm_inst.init_write(3, b'\x04\x04') 275 | wbm_inst.init_write(3, b'\x04\x44') 276 | wbm_inst.init_write(3, b'\x04\x33') 277 | wbm_inst.init_write(3, b'\x04\x22') 278 | wbm_inst.init_write(3, b'\x14\x11') 279 | 280 | yield wbm_inst.wait() 281 | yield clk.posedge 282 | 283 | while True: 284 | wbm_inst.init_read(0, 1) 285 | yield wbm_inst.wait() 286 | data = wbm_inst.get_read_data() 287 | if data[1][0] & 0x03 == 0: 288 | break 289 | 290 | data = i2c_mem_inst2.read_mem(0, 32) 291 | for i in range(0, len(data), 16): 292 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 293 | 294 | assert i2c_mem_inst2.read_mem(4,4) == b'\x44\x33\x22\x11' 295 | 296 | yield delay(100) 297 | 298 | yield clk.posedge 299 | print("test 4: read from slave 2") 300 | current_test.next = 4 301 | 302 | wbm_inst.init_write(2, b'\x51\x04\x00') 303 | wbm_inst.init_write(3, b'\x04\x04') 304 | wbm_inst.init_write(3, b'\x03') 305 | wbm_inst.init_write(3, b'\x02') 306 | wbm_inst.init_write(3, b'\x02') 307 | wbm_inst.init_write(3, b'\x12') 308 | 309 | yield wbm_inst.wait() 310 | yield clk.posedge 311 | 312 | while True: 313 | wbm_inst.init_read(0, 1) 314 | yield wbm_inst.wait() 315 | data = wbm_inst.get_read_data() 316 | if data[1][0] & 0x03 == 0: 317 | break 318 | 319 | wbm_inst.init_read(4, 1) 320 | wbm_inst.init_read(4, 1) 321 | wbm_inst.init_read(4, 1) 322 | wbm_inst.init_read(4, 1) 323 | 324 | yield wbm_inst.wait() 325 | yield clk.posedge 326 | 327 | data = wbm_inst.get_read_data() 328 | assert data[1] == b'\x44' 329 | 330 | data = wbm_inst.get_read_data() 331 | assert data[1] == b'\x33' 332 | 333 | data = wbm_inst.get_read_data() 334 | assert data[1] == b'\x22' 335 | 336 | data = wbm_inst.get_read_data() 337 | assert data[1] == b'\x11' 338 | 339 | yield delay(100) 340 | 341 | yield clk.posedge 342 | print("test 5: write to nonexistent device") 343 | current_test.next = 5 344 | 345 | wbm_inst.init_write(2, b'\x52\x04\x00') 346 | wbm_inst.init_write(3, b'\x04\x04') 347 | wbm_inst.init_write(3, b'\x04\xde') 348 | wbm_inst.init_write(3, b'\x04\xad') 349 | wbm_inst.init_write(3, b'\x04\xbe') 350 | wbm_inst.init_write(3, b'\x14\xef') 351 | 352 | yield wbm_inst.wait() 353 | yield clk.posedge 354 | 355 | got_missed_ack = False 356 | 357 | while True: 358 | wbm_inst.init_read(0, 1) 359 | yield wbm_inst.wait() 360 | data = wbm_inst.get_read_data() 361 | if data[1][0] & 0x08: 362 | got_missed_ack = True 363 | if data[1][0] & 0x03 == 0: 364 | break 365 | 366 | assert got_missed_ack 367 | 368 | yield delay(100) 369 | 370 | raise StopSimulation 371 | 372 | return instances() 373 | 374 | def test_bench(): 375 | sim = Simulation(bench()) 376 | sim.run() 377 | 378 | if __name__ == '__main__': 379 | print("Running test...") 380 | test_bench() 381 | -------------------------------------------------------------------------------- /tb/test_i2c_master_wbs_8.v: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2016-2017 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | // Language: Verilog 2001 26 | 27 | `timescale 1ns / 1ps 28 | 29 | /* 30 | * Testbench for i2c_master_wbs_8 31 | */ 32 | module test_i2c_master_wbs_8; 33 | 34 | // Parameters 35 | parameter DEFAULT_PRESCALE = 1; 36 | parameter FIXED_PRESCALE = 0; 37 | parameter CMD_FIFO = 1; 38 | parameter CMD_FIFO_DEPTH = 32; 39 | parameter WRITE_FIFO = 1; 40 | parameter WRITE_FIFO_DEPTH = 32; 41 | parameter READ_FIFO = 1; 42 | parameter READ_FIFO_DEPTH = 32; 43 | 44 | // Inputs 45 | reg clk = 0; 46 | reg rst = 0; 47 | reg [7:0] current_test = 0; 48 | 49 | reg [2:0] wbs_adr_i = 0; 50 | reg [7:0] wbs_dat_i = 0; 51 | reg wbs_we_i = 0; 52 | reg wbs_stb_i = 0; 53 | reg wbs_cyc_i = 0; 54 | reg i2c_scl_i = 1; 55 | reg i2c_sda_i = 1; 56 | 57 | // Outputs 58 | wire [7:0] wbs_dat_o; 59 | wire wbs_ack_o; 60 | wire i2c_scl_o; 61 | wire i2c_scl_t; 62 | wire i2c_sda_o; 63 | wire i2c_sda_t; 64 | 65 | initial begin 66 | // myhdl integration 67 | $from_myhdl( 68 | clk, 69 | rst, 70 | current_test, 71 | wbs_adr_i, 72 | wbs_dat_i, 73 | wbs_we_i, 74 | wbs_stb_i, 75 | wbs_cyc_i, 76 | i2c_scl_i, 77 | i2c_sda_i 78 | ); 79 | $to_myhdl( 80 | wbs_dat_o, 81 | wbs_ack_o, 82 | i2c_scl_o, 83 | i2c_scl_t, 84 | i2c_sda_o, 85 | i2c_sda_t 86 | ); 87 | 88 | // dump file 89 | $dumpfile("test_i2c_master_wbs_8.lxt"); 90 | $dumpvars(0, test_i2c_master_wbs_8); 91 | end 92 | 93 | i2c_master_wbs_8 #( 94 | .DEFAULT_PRESCALE(DEFAULT_PRESCALE), 95 | .FIXED_PRESCALE(FIXED_PRESCALE), 96 | .CMD_FIFO(CMD_FIFO), 97 | .CMD_FIFO_DEPTH(CMD_FIFO_DEPTH), 98 | .WRITE_FIFO(WRITE_FIFO), 99 | .WRITE_FIFO_DEPTH(WRITE_FIFO_DEPTH), 100 | .READ_FIFO(READ_FIFO), 101 | .READ_FIFO_DEPTH(READ_FIFO_DEPTH) 102 | ) 103 | UUT ( 104 | .clk(clk), 105 | .rst(rst), 106 | .wbs_adr_i(wbs_adr_i), 107 | .wbs_dat_i(wbs_dat_i), 108 | .wbs_dat_o(wbs_dat_o), 109 | .wbs_we_i(wbs_we_i), 110 | .wbs_stb_i(wbs_stb_i), 111 | .wbs_ack_o(wbs_ack_o), 112 | .wbs_cyc_i(wbs_cyc_i), 113 | .i2c_scl_i(i2c_scl_i), 114 | .i2c_scl_o(i2c_scl_o), 115 | .i2c_scl_t(i2c_scl_t), 116 | .i2c_sda_i(i2c_sda_i), 117 | .i2c_sda_o(i2c_sda_o), 118 | .i2c_sda_t(i2c_sda_t) 119 | ); 120 | 121 | endmodule 122 | -------------------------------------------------------------------------------- /tb/test_i2c_slave.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | Copyright (c) 2017 Alex Forencich 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | """ 25 | 26 | from myhdl import * 27 | import os 28 | 29 | import axis_ep 30 | import i2c 31 | 32 | module = 'i2c_slave' 33 | testbench = 'test_%s' % module 34 | 35 | srcs = [] 36 | 37 | srcs.append("../rtl/%s.v" % module) 38 | srcs.append("%s.v" % testbench) 39 | 40 | src = ' '.join(srcs) 41 | 42 | build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) 43 | 44 | def bench(): 45 | 46 | # Parameters 47 | FILTER_LEN = 1 48 | 49 | # Inputs 50 | clk = Signal(bool(0)) 51 | rst = Signal(bool(0)) 52 | current_test = Signal(intbv(0)[8:]) 53 | 54 | release_bus = Signal(bool(0)) 55 | s_axis_data_tdata = Signal(intbv(0)[8:]) 56 | s_axis_data_tvalid = Signal(bool(0)) 57 | s_axis_data_tlast = Signal(bool(0)) 58 | m_axis_data_tready = Signal(bool(0)) 59 | scl_i = Signal(bool(1)) 60 | sda_i = Signal(bool(1)) 61 | enable = Signal(bool(0)) 62 | device_address = Signal(intbv(0)[7:]) 63 | device_address_mask = Signal(intbv(0x7f)[7:]) 64 | 65 | m_scl_i = Signal(bool(1)) 66 | m_sda_i = Signal(bool(1)) 67 | 68 | s2_scl_i = Signal(bool(1)) 69 | s2_sda_i = Signal(bool(1)) 70 | 71 | # Outputs 72 | s_axis_data_tready = Signal(bool(0)) 73 | m_axis_data_tdata = Signal(intbv(0)[8:]) 74 | m_axis_data_tvalid = Signal(bool(0)) 75 | m_axis_data_tlast = Signal(bool(0)) 76 | scl_o = Signal(bool(1)) 77 | scl_t = Signal(bool(1)) 78 | sda_o = Signal(bool(1)) 79 | sda_t = Signal(bool(1)) 80 | busy = Signal(bool(0)) 81 | bus_address = Signal(intbv(0)[7:]) 82 | bus_addressed = Signal(bool(0)) 83 | bus_active = Signal(bool(0)) 84 | 85 | m_scl_o = Signal(bool(1)) 86 | m_scl_t = Signal(bool(1)) 87 | m_sda_o = Signal(bool(1)) 88 | m_sda_t = Signal(bool(1)) 89 | 90 | s2_scl_o = Signal(bool(1)) 91 | s2_scl_t = Signal(bool(1)) 92 | s2_sda_o = Signal(bool(1)) 93 | s2_sda_t = Signal(bool(1)) 94 | 95 | # sources and sinks 96 | data_source_pause = Signal(bool(0)) 97 | data_sink_pause = Signal(bool(0)) 98 | 99 | data_source = axis_ep.AXIStreamSource() 100 | 101 | data_source_logic = data_source.create_logic( 102 | clk, 103 | rst, 104 | tdata=s_axis_data_tdata, 105 | tvalid=s_axis_data_tvalid, 106 | tready=s_axis_data_tready, 107 | tlast=s_axis_data_tlast, 108 | pause=data_source_pause, 109 | name='data_source' 110 | ) 111 | 112 | data_sink = axis_ep.AXIStreamSink() 113 | 114 | data_sink_logic = data_sink.create_logic( 115 | clk, 116 | rst, 117 | tdata=m_axis_data_tdata, 118 | tvalid=m_axis_data_tvalid, 119 | tready=m_axis_data_tready, 120 | tlast=m_axis_data_tlast, 121 | pause=data_sink_pause, 122 | name='data_sink' 123 | ) 124 | 125 | # I2C master 126 | i2c_master_inst = i2c.I2CMaster() 127 | 128 | i2c_master_logic = i2c_master_inst.create_logic( 129 | clk, 130 | rst, 131 | scl_i=m_scl_i, 132 | scl_o=m_scl_o, 133 | scl_t=m_scl_t, 134 | sda_i=m_sda_i, 135 | sda_o=m_sda_o, 136 | sda_t=m_sda_t, 137 | prescale=4, 138 | name='master' 139 | ) 140 | 141 | # I2C memory model 2 142 | i2c_mem_inst2 = i2c.I2CMem(1024) 143 | 144 | i2c_mem_logic2 = i2c_mem_inst2.create_logic( 145 | scl_i=s2_scl_i, 146 | scl_o=s2_scl_o, 147 | scl_t=s2_scl_t, 148 | sda_i=s2_sda_i, 149 | sda_o=s2_sda_o, 150 | sda_t=s2_sda_t, 151 | abw=2, 152 | address=0x51, 153 | latency=0, 154 | name='slave2' 155 | ) 156 | 157 | # DUT 158 | if os.system(build_cmd): 159 | raise Exception("Error running build command") 160 | 161 | dut = Cosimulation( 162 | "vvp -m myhdl %s.vvp -lxt2" % testbench, 163 | clk=clk, 164 | rst=rst, 165 | current_test=current_test, 166 | release_bus=release_bus, 167 | s_axis_data_tdata=s_axis_data_tdata, 168 | s_axis_data_tvalid=s_axis_data_tvalid, 169 | s_axis_data_tready=s_axis_data_tready, 170 | s_axis_data_tlast=s_axis_data_tlast, 171 | m_axis_data_tdata=m_axis_data_tdata, 172 | m_axis_data_tvalid=m_axis_data_tvalid, 173 | m_axis_data_tready=m_axis_data_tready, 174 | m_axis_data_tlast=m_axis_data_tlast, 175 | scl_i=scl_i, 176 | scl_o=scl_o, 177 | scl_t=scl_t, 178 | sda_i=sda_i, 179 | sda_o=sda_o, 180 | sda_t=sda_t, 181 | busy=busy, 182 | bus_address=bus_address, 183 | bus_addressed=bus_addressed, 184 | bus_active=bus_active, 185 | enable=enable, 186 | device_address=device_address, 187 | device_address_mask=device_address_mask 188 | ) 189 | 190 | @always_comb 191 | def bus(): 192 | # emulate I2C wired AND 193 | m_scl_i.next = m_scl_o & scl_o & s2_scl_o; 194 | m_sda_i.next = m_sda_o & sda_o & s2_sda_o; 195 | 196 | scl_i.next = m_scl_o & scl_o & s2_scl_o; 197 | sda_i.next = m_sda_o & sda_o & s2_sda_o; 198 | 199 | s2_scl_i.next = m_scl_o & scl_o & s2_scl_o; 200 | s2_sda_i.next = m_sda_o & sda_o & s2_sda_o; 201 | 202 | @always(delay(4)) 203 | def clkgen(): 204 | clk.next = not clk 205 | 206 | @instance 207 | def check(): 208 | yield delay(100) 209 | yield clk.posedge 210 | rst.next = 1 211 | yield clk.posedge 212 | rst.next = 0 213 | yield clk.posedge 214 | yield delay(100) 215 | yield clk.posedge 216 | 217 | # testbench stimulus 218 | 219 | enable.next = 1 220 | device_address.next = 0x50 221 | device_address_mask.next = 0x7f 222 | 223 | yield clk.posedge 224 | print("test 1: write") 225 | current_test.next = 1 226 | 227 | i2c_master_inst.init_write(0x50, b'\x00\x04'+b'\x11\x22\x33\x44') 228 | 229 | yield i2c_master_inst.wait() 230 | yield clk.posedge 231 | 232 | data = None 233 | while not data: 234 | yield clk.posedge 235 | data = data_sink.recv() 236 | 237 | assert data.data == b'\x00\x04'+b'\x11\x22\x33\x44' 238 | 239 | yield delay(100) 240 | 241 | yield clk.posedge 242 | print("test 2: read") 243 | current_test.next = 2 244 | 245 | i2c_master_inst.init_write(0x50, b'\x00\x04') 246 | i2c_master_inst.init_read(0x50, 4) 247 | 248 | data_source.send(b'\x11\x22\x33\x44') 249 | 250 | yield i2c_master_inst.wait() 251 | yield clk.posedge 252 | 253 | data = None 254 | while not data: 255 | yield clk.posedge 256 | data = data_sink.recv() 257 | 258 | assert data.data == b'\x00\x04' 259 | 260 | data = i2c_master_inst.get_read_data() 261 | assert data[0] == 0x50 262 | assert data[1] == b'\x11\x22\x33\x44' 263 | 264 | yield delay(100) 265 | 266 | yield clk.posedge 267 | print("test 3: read with delays") 268 | current_test.next = 3 269 | 270 | i2c_master_inst.init_write(0x50, b'\x00\x04') 271 | i2c_master_inst.init_read(0x50, 4) 272 | 273 | data_source.send(b'\x11\x22\x33\x44') 274 | 275 | data_source_pause.next = True 276 | data_sink_pause.next = True 277 | 278 | yield delay(5000) 279 | data_sink_pause.next = False 280 | 281 | yield delay(2000) 282 | data_source_pause.next = False 283 | 284 | yield i2c_master_inst.wait() 285 | yield clk.posedge 286 | 287 | data = None 288 | while not data: 289 | yield clk.posedge 290 | data = data_sink.recv() 291 | 292 | assert data.data == b'\x00\x04' 293 | 294 | data = i2c_master_inst.get_read_data() 295 | assert data[0] == 0x50 296 | assert data[1] == b'\x11\x22\x33\x44' 297 | 298 | yield delay(100) 299 | 300 | yield clk.posedge 301 | print("test 4: access slave 2") 302 | current_test.next = 4 303 | 304 | i2c_master_inst.init_write(0x51, b'\x00\x04'+b'\x11\x22\x33\x44') 305 | 306 | yield i2c_master_inst.wait() 307 | yield clk.posedge 308 | 309 | data = i2c_mem_inst2.read_mem(0, 32) 310 | for i in range(0, len(data), 16): 311 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 312 | 313 | assert i2c_mem_inst2.read_mem(4,4) == b'\x11\x22\x33\x44' 314 | 315 | i2c_master_inst.init_write(0x51, b'\x00\x04') 316 | i2c_master_inst.init_read(0x51, 4) 317 | 318 | yield i2c_master_inst.wait() 319 | yield clk.posedge 320 | 321 | data = i2c_master_inst.get_read_data() 322 | assert data[0] == 0x51 323 | assert data[1] == b'\x11\x22\x33\x44' 324 | 325 | yield delay(100) 326 | 327 | raise StopSimulation 328 | 329 | return instances() 330 | 331 | def test_bench(): 332 | sim = Simulation(bench()) 333 | sim.run() 334 | 335 | if __name__ == '__main__': 336 | print("Running test...") 337 | test_bench() 338 | -------------------------------------------------------------------------------- /tb/test_i2c_slave.v: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2017 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | // Language: Verilog 2001 26 | 27 | `timescale 1ns / 1ps 28 | 29 | /* 30 | * Testbench for i2c_slave 31 | */ 32 | module test_i2c_slave; 33 | 34 | // Parameters 35 | parameter FILTER_LEN = 2; 36 | 37 | // Inputs 38 | reg clk = 0; 39 | reg rst = 0; 40 | reg [7:0] current_test = 0; 41 | 42 | reg release_bus = 0; 43 | reg [7:0] s_axis_data_tdata = 0; 44 | reg s_axis_data_tvalid = 0; 45 | reg s_axis_data_tlast = 0; 46 | reg m_axis_data_tready = 0; 47 | reg scl_i = 1; 48 | reg sda_i = 1; 49 | reg enable = 0; 50 | reg [6:0] device_address = 0; 51 | reg [6:0] device_address_mask = 0; 52 | 53 | // Outputs 54 | wire s_axis_data_tready; 55 | wire [7:0] m_axis_data_tdata; 56 | wire m_axis_data_tvalid; 57 | wire m_axis_data_tlast; 58 | wire scl_o; 59 | wire scl_t; 60 | wire sda_o; 61 | wire sda_t; 62 | wire busy; 63 | wire [6:0] bus_address; 64 | wire bus_addressed; 65 | wire bus_active; 66 | 67 | initial begin 68 | // myhdl integration 69 | $from_myhdl( 70 | clk, 71 | rst, 72 | current_test, 73 | release_bus, 74 | s_axis_data_tdata, 75 | s_axis_data_tvalid, 76 | s_axis_data_tlast, 77 | m_axis_data_tready, 78 | scl_i, 79 | sda_i, 80 | enable, 81 | device_address, 82 | device_address_mask 83 | ); 84 | $to_myhdl( 85 | s_axis_data_tready, 86 | m_axis_data_tdata, 87 | m_axis_data_tvalid, 88 | m_axis_data_tlast, 89 | scl_o, 90 | scl_t, 91 | sda_o, 92 | sda_t, 93 | busy, 94 | bus_address, 95 | bus_addressed, 96 | bus_active 97 | ); 98 | 99 | // dump file 100 | $dumpfile("test_i2c_slave.lxt"); 101 | $dumpvars(0, test_i2c_slave); 102 | end 103 | 104 | i2c_slave #( 105 | .FILTER_LEN(FILTER_LEN) 106 | ) 107 | UUT ( 108 | .clk(clk), 109 | .rst(rst), 110 | .release_bus(release_bus), 111 | .s_axis_data_tdata(s_axis_data_tdata), 112 | .s_axis_data_tvalid(s_axis_data_tvalid), 113 | .s_axis_data_tready(s_axis_data_tready), 114 | .s_axis_data_tlast(s_axis_data_tlast), 115 | .m_axis_data_tdata(m_axis_data_tdata), 116 | .m_axis_data_tvalid(m_axis_data_tvalid), 117 | .m_axis_data_tready(m_axis_data_tready), 118 | .m_axis_data_tlast(m_axis_data_tlast), 119 | .scl_i(scl_i), 120 | .scl_o(scl_o), 121 | .scl_t(scl_t), 122 | .sda_i(sda_i), 123 | .sda_o(sda_o), 124 | .sda_t(sda_t), 125 | .busy(busy), 126 | .bus_address(bus_address), 127 | .bus_addressed(bus_addressed), 128 | .bus_active(bus_active), 129 | .enable(enable), 130 | .device_address(device_address), 131 | .device_address_mask(device_address_mask) 132 | ); 133 | 134 | endmodule 135 | -------------------------------------------------------------------------------- /tb/test_i2c_slave_axil_master.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | Copyright (c) 2019 Alex Forencich 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | """ 25 | 26 | from myhdl import * 27 | import os 28 | import struct 29 | 30 | import i2c 31 | import axil 32 | 33 | module = 'i2c_slave_axil_master' 34 | testbench = 'test_%s' % module 35 | 36 | srcs = [] 37 | 38 | srcs.append("../rtl/%s.v" % module) 39 | srcs.append("../rtl/i2c_slave.v") 40 | srcs.append("%s.v" % testbench) 41 | 42 | src = ' '.join(srcs) 43 | 44 | build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) 45 | 46 | def bench(): 47 | 48 | # Parameters 49 | FILTER_LEN = 4 50 | DATA_WIDTH = 32 51 | ADDR_WIDTH = 16 52 | STRB_WIDTH = (DATA_WIDTH/8) 53 | 54 | # Inputs 55 | clk = Signal(bool(0)) 56 | rst = Signal(bool(0)) 57 | current_test = Signal(intbv(0)[8:]) 58 | 59 | i2c_scl_i = Signal(bool(1)) 60 | i2c_sda_i = Signal(bool(1)) 61 | m_axil_awready = Signal(bool(0)) 62 | m_axil_wready = Signal(bool(0)) 63 | m_axil_bresp = Signal(intbv(0)[2:]) 64 | m_axil_bvalid = Signal(bool(0)) 65 | m_axil_arready = Signal(bool(0)) 66 | m_axil_rdata = Signal(intbv(0)[DATA_WIDTH:]) 67 | m_axil_rresp = Signal(intbv(0)[2:]) 68 | m_axil_rvalid = Signal(bool(0)) 69 | enable = Signal(bool(0)) 70 | device_address = Signal(intbv(0)[7:]) 71 | 72 | m_scl_i = Signal(bool(1)) 73 | m_sda_i = Signal(bool(1)) 74 | 75 | s2_scl_i = Signal(bool(1)) 76 | s2_sda_i = Signal(bool(1)) 77 | 78 | # Outputs 79 | i2c_scl_o = Signal(bool(1)) 80 | i2c_scl_t = Signal(bool(1)) 81 | i2c_sda_o = Signal(bool(1)) 82 | i2c_sda_t = Signal(bool(1)) 83 | m_axil_awaddr = Signal(intbv(0)[ADDR_WIDTH:]) 84 | m_axil_awprot = Signal(intbv(0)[3:]) 85 | m_axil_awvalid = Signal(bool(0)) 86 | m_axil_wdata = Signal(intbv(0)[DATA_WIDTH:]) 87 | m_axil_wstrb = Signal(intbv(0)[STRB_WIDTH:]) 88 | m_axil_wvalid = Signal(bool(0)) 89 | m_axil_bready = Signal(bool(0)) 90 | m_axil_araddr = Signal(intbv(0)[ADDR_WIDTH:]) 91 | m_axil_arprot = Signal(intbv(0)[3:]) 92 | m_axil_arvalid = Signal(bool(0)) 93 | m_axil_rready = Signal(bool(0)) 94 | busy = Signal(bool(0)) 95 | bus_addressed = Signal(bool(0)) 96 | bus_active = Signal(bool(0)) 97 | 98 | m_scl_o = Signal(bool(1)) 99 | m_scl_t = Signal(bool(1)) 100 | m_sda_o = Signal(bool(1)) 101 | m_sda_t = Signal(bool(1)) 102 | 103 | s2_scl_o = Signal(bool(1)) 104 | s2_scl_t = Signal(bool(1)) 105 | s2_sda_o = Signal(bool(1)) 106 | s2_sda_t = Signal(bool(1)) 107 | 108 | # I2C master 109 | i2c_master_inst = i2c.I2CMaster() 110 | 111 | i2c_master_logic = i2c_master_inst.create_logic( 112 | clk, 113 | rst, 114 | scl_i=m_scl_i, 115 | scl_o=m_scl_o, 116 | scl_t=m_scl_t, 117 | sda_i=m_sda_i, 118 | sda_o=m_sda_o, 119 | sda_t=m_sda_t, 120 | prescale=4, 121 | name='master' 122 | ) 123 | 124 | # I2C memory model 2 125 | i2c_mem_inst2 = i2c.I2CMem(1024) 126 | 127 | i2c_mem_logic2 = i2c_mem_inst2.create_logic( 128 | scl_i=s2_scl_i, 129 | scl_o=s2_scl_o, 130 | scl_t=s2_scl_t, 131 | sda_i=s2_sda_i, 132 | sda_o=s2_sda_o, 133 | sda_t=s2_sda_t, 134 | abw=2, 135 | address=0x51, 136 | latency=0, 137 | name='slave2' 138 | ) 139 | 140 | # AXI4-Lite RAM model 141 | axil_ram_inst = axil.AXILiteRam(2**16) 142 | axil_ram_pause = Signal(bool(False)) 143 | 144 | axil_ram_port0 = axil_ram_inst.create_port( 145 | clk, 146 | s_axil_awaddr=m_axil_awaddr, 147 | s_axil_awprot=m_axil_awprot, 148 | s_axil_awvalid=m_axil_awvalid, 149 | s_axil_awready=m_axil_awready, 150 | s_axil_wdata=m_axil_wdata, 151 | s_axil_wstrb=m_axil_wstrb, 152 | s_axil_wvalid=m_axil_wvalid, 153 | s_axil_wready=m_axil_wready, 154 | s_axil_bresp=m_axil_bresp, 155 | s_axil_bvalid=m_axil_bvalid, 156 | s_axil_bready=m_axil_bready, 157 | s_axil_araddr=m_axil_araddr, 158 | s_axil_arprot=m_axil_arprot, 159 | s_axil_arvalid=m_axil_arvalid, 160 | s_axil_arready=m_axil_arready, 161 | s_axil_rdata=m_axil_rdata, 162 | s_axil_rresp=m_axil_rresp, 163 | s_axil_rvalid=m_axil_rvalid, 164 | s_axil_rready=m_axil_rready, 165 | pause=axil_ram_pause, 166 | name='port0' 167 | ) 168 | 169 | # DUT 170 | if os.system(build_cmd): 171 | raise Exception("Error running build command") 172 | 173 | dut = Cosimulation( 174 | "vvp -m myhdl %s.vvp -lxt2" % testbench, 175 | clk=clk, 176 | rst=rst, 177 | current_test=current_test, 178 | i2c_scl_i=i2c_scl_i, 179 | i2c_scl_o=i2c_scl_o, 180 | i2c_scl_t=i2c_scl_t, 181 | i2c_sda_i=i2c_sda_i, 182 | i2c_sda_o=i2c_sda_o, 183 | i2c_sda_t=i2c_sda_t, 184 | m_axil_awaddr=m_axil_awaddr, 185 | m_axil_awprot=m_axil_awprot, 186 | m_axil_awvalid=m_axil_awvalid, 187 | m_axil_awready=m_axil_awready, 188 | m_axil_wdata=m_axil_wdata, 189 | m_axil_wstrb=m_axil_wstrb, 190 | m_axil_wvalid=m_axil_wvalid, 191 | m_axil_wready=m_axil_wready, 192 | m_axil_bresp=m_axil_bresp, 193 | m_axil_bvalid=m_axil_bvalid, 194 | m_axil_bready=m_axil_bready, 195 | m_axil_araddr=m_axil_araddr, 196 | m_axil_arprot=m_axil_arprot, 197 | m_axil_arvalid=m_axil_arvalid, 198 | m_axil_arready=m_axil_arready, 199 | m_axil_rdata=m_axil_rdata, 200 | m_axil_rresp=m_axil_rresp, 201 | m_axil_rvalid=m_axil_rvalid, 202 | m_axil_rready=m_axil_rready, 203 | busy=busy, 204 | bus_addressed=bus_addressed, 205 | bus_active=bus_active, 206 | enable=enable, 207 | device_address=device_address 208 | ) 209 | 210 | @always_comb 211 | def bus(): 212 | # emulate I2C wired AND 213 | scl = m_scl_o & i2c_scl_o & s2_scl_o 214 | sda = m_sda_o & i2c_sda_o & s2_sda_o 215 | 216 | m_scl_i.next = scl; 217 | m_sda_i.next = sda; 218 | 219 | i2c_scl_i.next = scl 220 | i2c_sda_i.next = sda 221 | 222 | s2_scl_i.next = scl 223 | s2_sda_i.next = sda 224 | 225 | @always(delay(4)) 226 | def clkgen(): 227 | clk.next = not clk 228 | 229 | @instance 230 | def check(): 231 | yield delay(100) 232 | yield clk.posedge 233 | rst.next = 1 234 | yield clk.posedge 235 | rst.next = 0 236 | yield clk.posedge 237 | yield delay(100) 238 | yield clk.posedge 239 | 240 | # testbench stimulus 241 | 242 | enable.next = 1 243 | device_address.next = 0x50 244 | 245 | yield clk.posedge 246 | print("test 1: write") 247 | current_test.next = 1 248 | 249 | i2c_master_inst.init_write(0x50, b'\x00\x04'+b'\x11\x22\x33\x44') 250 | 251 | yield i2c_master_inst.wait() 252 | yield clk.posedge 253 | 254 | while busy: 255 | yield clk.posedge 256 | 257 | data = axil_ram_inst.read_mem(0, 32) 258 | for i in range(0, len(data), 16): 259 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 260 | 261 | assert axil_ram_inst.read_mem(4,4) == b'\x11\x22\x33\x44' 262 | 263 | yield delay(100) 264 | 265 | yield clk.posedge 266 | print("test 2: read") 267 | current_test.next = 2 268 | 269 | i2c_master_inst.init_write(0x50, b'\x00\x04') 270 | i2c_master_inst.init_read(0x50, 4) 271 | 272 | yield i2c_master_inst.wait() 273 | yield clk.posedge 274 | 275 | data = i2c_master_inst.get_read_data() 276 | assert data[0] == 0x50 277 | assert data[1] == b'\x11\x22\x33\x44' 278 | 279 | yield delay(100) 280 | 281 | yield clk.posedge 282 | print("test 3: various writes") 283 | current_test.next = 3 284 | 285 | for length in range(1,9): 286 | for offset in range(4): 287 | i2c_master_inst.init_write(0x50, bytearray(struct.pack('>H', 256*(16*offset+length)+offset)+b'\x11\x22\x33\x44\x55\x66\x77\x88'[0:length])) 288 | 289 | yield i2c_master_inst.wait() 290 | yield clk.posedge 291 | 292 | while busy: 293 | yield clk.posedge 294 | 295 | data = axil_ram_inst.read_mem(256*(16*offset+length), 32) 296 | for i in range(0, len(data), 16): 297 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 298 | 299 | assert axil_ram_inst.read_mem(256*(16*offset+length)+offset,length) == b'\x11\x22\x33\x44\x55\x66\x77\x88'[0:length] 300 | 301 | yield delay(100) 302 | 303 | yield clk.posedge 304 | print("test 4: various reads") 305 | current_test.next = 4 306 | 307 | for length in range(1,9): 308 | for offset in range(4): 309 | i2c_master_inst.init_write(0x50, bytearray(struct.pack('>H', 256*(16*offset+length)+offset))) 310 | i2c_master_inst.init_read(0x50, length) 311 | 312 | yield i2c_master_inst.wait() 313 | yield clk.posedge 314 | 315 | data = i2c_master_inst.get_read_data() 316 | assert data[0] == 0x50 317 | assert data[1] == b'\x11\x22\x33\x44\x55\x66\x77\x88'[0:length] 318 | 319 | yield delay(100) 320 | 321 | # TODO various reads and writes 322 | 323 | # yield clk.posedge 324 | # print("test 3: read with delays") 325 | # current_test.next = 3 326 | 327 | # i2c_master_inst.init_write(0x50, b'\x00\x04') 328 | # i2c_master_inst.init_read(0x50, 4) 329 | 330 | # data_source.send(b'\x11\x22\x33\x44') 331 | 332 | # data_source_pause.next = True 333 | # data_sink_pause.next = True 334 | 335 | # yield delay(5000) 336 | # data_sink_pause.next = False 337 | 338 | # yield delay(2000) 339 | # data_source_pause.next = False 340 | 341 | # yield i2c_master_inst.wait() 342 | # yield clk.posedge 343 | 344 | # data = None 345 | # while not data: 346 | # yield clk.posedge 347 | # data = data_sink.recv() 348 | 349 | # assert data.data == b'\x00\x04' 350 | 351 | # data = i2c_master_inst.get_read_data() 352 | # assert data[0] == 0x50 353 | # assert data[1] == b'\x11\x22\x33\x44' 354 | 355 | # yield delay(100) 356 | 357 | yield clk.posedge 358 | print("test 4: access slave 2") 359 | current_test.next = 4 360 | 361 | i2c_master_inst.init_write(0x51, b'\x00\x04'+b'\x11\x22\x33\x44') 362 | 363 | yield i2c_master_inst.wait() 364 | yield clk.posedge 365 | 366 | data = i2c_mem_inst2.read_mem(0, 32) 367 | for i in range(0, len(data), 16): 368 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 369 | 370 | assert i2c_mem_inst2.read_mem(4,4) == b'\x11\x22\x33\x44' 371 | 372 | i2c_master_inst.init_write(0x51, b'\x00\x04') 373 | i2c_master_inst.init_read(0x51, 4) 374 | 375 | yield i2c_master_inst.wait() 376 | yield clk.posedge 377 | 378 | data = i2c_master_inst.get_read_data() 379 | assert data[0] == 0x51 380 | assert data[1] == b'\x11\x22\x33\x44' 381 | 382 | yield delay(100) 383 | 384 | raise StopSimulation 385 | 386 | return instances() 387 | 388 | def test_bench(): 389 | sim = Simulation(bench()) 390 | sim.run() 391 | 392 | if __name__ == '__main__': 393 | print("Running test...") 394 | test_bench() 395 | -------------------------------------------------------------------------------- /tb/test_i2c_slave_axil_master.v: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2019 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | // Language: Verilog 2001 26 | 27 | `timescale 1ns / 1ps 28 | 29 | /* 30 | * Testbench for i2c_slave_axil_master 31 | */ 32 | module test_i2c_slave_axil_master; 33 | 34 | // Parameters 35 | parameter FILTER_LEN = 4; 36 | parameter DATA_WIDTH = 32; 37 | parameter ADDR_WIDTH = 16; 38 | parameter STRB_WIDTH = (DATA_WIDTH/8); 39 | 40 | // Inputs 41 | reg clk = 0; 42 | reg rst = 0; 43 | reg [7:0] current_test = 0; 44 | 45 | reg i2c_scl_i = 1; 46 | reg i2c_sda_i = 1; 47 | reg m_axil_awready = 0; 48 | reg m_axil_wready = 0; 49 | reg [1:0] m_axil_bresp = 0; 50 | reg m_axil_bvalid = 0; 51 | reg m_axil_arready = 0; 52 | reg [DATA_WIDTH-1:0] m_axil_rdata = 0; 53 | reg [1:0] m_axil_rresp = 0; 54 | reg m_axil_rvalid = 0; 55 | reg enable = 0; 56 | reg [6:0] device_address = 0; 57 | 58 | // Outputs 59 | wire i2c_scl_o; 60 | wire i2c_scl_t; 61 | wire i2c_sda_o; 62 | wire i2c_sda_t; 63 | wire [ADDR_WIDTH-1:0] m_axil_awaddr; 64 | wire [2:0] m_axil_awprot; 65 | wire m_axil_awvalid; 66 | wire [DATA_WIDTH-1:0] m_axil_wdata; 67 | wire [STRB_WIDTH-1:0] m_axil_wstrb; 68 | wire m_axil_wvalid; 69 | wire m_axil_bready; 70 | wire [ADDR_WIDTH-1:0] m_axil_araddr; 71 | wire [2:0] m_axil_arprot; 72 | wire m_axil_arvalid; 73 | wire m_axil_rready; 74 | wire busy; 75 | wire bus_addressed; 76 | wire bus_active; 77 | 78 | initial begin 79 | // myhdl integration 80 | $from_myhdl( 81 | clk, 82 | rst, 83 | current_test, 84 | i2c_scl_i, 85 | i2c_sda_i, 86 | m_axil_awready, 87 | m_axil_wready, 88 | m_axil_bresp, 89 | m_axil_bvalid, 90 | m_axil_arready, 91 | m_axil_rdata, 92 | m_axil_rresp, 93 | m_axil_rvalid, 94 | enable, 95 | device_address 96 | ); 97 | $to_myhdl( 98 | i2c_scl_o, 99 | i2c_scl_t, 100 | i2c_sda_o, 101 | i2c_sda_t, 102 | m_axil_awaddr, 103 | m_axil_awprot, 104 | m_axil_awvalid, 105 | m_axil_wdata, 106 | m_axil_wstrb, 107 | m_axil_wvalid, 108 | m_axil_bready, 109 | m_axil_araddr, 110 | m_axil_arprot, 111 | m_axil_arvalid, 112 | m_axil_rready, 113 | busy, 114 | bus_addressed, 115 | bus_active 116 | ); 117 | 118 | // dump file 119 | $dumpfile("test_i2c_slave_axil_master.lxt"); 120 | $dumpvars(0, test_i2c_slave_axil_master); 121 | end 122 | 123 | i2c_slave_axil_master #( 124 | .FILTER_LEN(FILTER_LEN), 125 | .DATA_WIDTH(DATA_WIDTH), 126 | .ADDR_WIDTH(ADDR_WIDTH), 127 | .STRB_WIDTH(STRB_WIDTH) 128 | ) 129 | UUT ( 130 | .clk(clk), 131 | .rst(rst), 132 | .i2c_scl_i(i2c_scl_i), 133 | .i2c_scl_o(i2c_scl_o), 134 | .i2c_scl_t(i2c_scl_t), 135 | .i2c_sda_i(i2c_sda_i), 136 | .i2c_sda_o(i2c_sda_o), 137 | .i2c_sda_t(i2c_sda_t), 138 | .m_axil_awaddr(m_axil_awaddr), 139 | .m_axil_awprot(m_axil_awprot), 140 | .m_axil_awvalid(m_axil_awvalid), 141 | .m_axil_awready(m_axil_awready), 142 | .m_axil_wdata(m_axil_wdata), 143 | .m_axil_wstrb(m_axil_wstrb), 144 | .m_axil_wvalid(m_axil_wvalid), 145 | .m_axil_wready(m_axil_wready), 146 | .m_axil_bresp(m_axil_bresp), 147 | .m_axil_bvalid(m_axil_bvalid), 148 | .m_axil_bready(m_axil_bready), 149 | .m_axil_araddr(m_axil_araddr), 150 | .m_axil_arprot(m_axil_arprot), 151 | .m_axil_arvalid(m_axil_arvalid), 152 | .m_axil_arready(m_axil_arready), 153 | .m_axil_rdata(m_axil_rdata), 154 | .m_axil_rresp(m_axil_rresp), 155 | .m_axil_rvalid(m_axil_rvalid), 156 | .m_axil_rready(m_axil_rready), 157 | .busy(busy), 158 | .bus_addressed(bus_addressed), 159 | .bus_active(bus_active), 160 | .enable(enable), 161 | .device_address(device_address) 162 | ); 163 | 164 | endmodule 165 | -------------------------------------------------------------------------------- /tb/test_i2c_slave_wbm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | Copyright (c) 2017 Alex Forencich 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | """ 25 | 26 | from myhdl import * 27 | import os 28 | import struct 29 | 30 | import i2c 31 | import wb 32 | 33 | module = 'i2c_slave_wbm' 34 | testbench = 'test_%s' % module 35 | 36 | srcs = [] 37 | 38 | srcs.append("../rtl/%s.v" % module) 39 | srcs.append("../rtl/i2c_slave.v") 40 | srcs.append("%s.v" % testbench) 41 | 42 | src = ' '.join(srcs) 43 | 44 | build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) 45 | 46 | def bench(): 47 | 48 | # Parameters 49 | FILTER_LEN = 4 50 | WB_DATA_WIDTH = 32 51 | WB_ADDR_WIDTH = 16 52 | WB_SELECT_WIDTH = WB_DATA_WIDTH/8 53 | 54 | # Inputs 55 | clk = Signal(bool(0)) 56 | rst = Signal(bool(0)) 57 | current_test = Signal(intbv(0)[8:]) 58 | 59 | i2c_scl_i = Signal(bool(1)) 60 | i2c_sda_i = Signal(bool(1)) 61 | wb_dat_i = Signal(intbv(0)[WB_DATA_WIDTH:]) 62 | wb_ack_i = Signal(bool(0)) 63 | wb_err_i = Signal(bool(0)) 64 | enable = Signal(bool(0)) 65 | device_address = Signal(intbv(0)[7:]) 66 | 67 | m_scl_i = Signal(bool(1)) 68 | m_sda_i = Signal(bool(1)) 69 | 70 | s2_scl_i = Signal(bool(1)) 71 | s2_sda_i = Signal(bool(1)) 72 | 73 | # Outputs 74 | i2c_scl_o = Signal(bool(1)) 75 | i2c_scl_t = Signal(bool(1)) 76 | i2c_sda_o = Signal(bool(1)) 77 | i2c_sda_t = Signal(bool(1)) 78 | wb_adr_o = Signal(intbv(0)[WB_ADDR_WIDTH:]) 79 | wb_dat_o = Signal(intbv(0)[WB_DATA_WIDTH:]) 80 | wb_we_o = Signal(bool(0)) 81 | wb_sel_o = Signal(intbv(0)[WB_SELECT_WIDTH:]) 82 | wb_stb_o = Signal(bool(0)) 83 | wb_cyc_o = Signal(bool(0)) 84 | busy = Signal(bool(0)) 85 | bus_addressed = Signal(bool(0)) 86 | bus_active = Signal(bool(0)) 87 | 88 | m_scl_o = Signal(bool(1)) 89 | m_scl_t = Signal(bool(1)) 90 | m_sda_o = Signal(bool(1)) 91 | m_sda_t = Signal(bool(1)) 92 | 93 | s2_scl_o = Signal(bool(1)) 94 | s2_scl_t = Signal(bool(1)) 95 | s2_sda_o = Signal(bool(1)) 96 | s2_sda_t = Signal(bool(1)) 97 | 98 | # I2C master 99 | i2c_master_inst = i2c.I2CMaster() 100 | 101 | i2c_master_logic = i2c_master_inst.create_logic( 102 | clk, 103 | rst, 104 | scl_i=m_scl_i, 105 | scl_o=m_scl_o, 106 | scl_t=m_scl_t, 107 | sda_i=m_sda_i, 108 | sda_o=m_sda_o, 109 | sda_t=m_sda_t, 110 | prescale=4, 111 | name='master' 112 | ) 113 | 114 | # I2C memory model 2 115 | i2c_mem_inst2 = i2c.I2CMem(1024) 116 | 117 | i2c_mem_logic2 = i2c_mem_inst2.create_logic( 118 | scl_i=s2_scl_i, 119 | scl_o=s2_scl_o, 120 | scl_t=s2_scl_t, 121 | sda_i=s2_sda_i, 122 | sda_o=s2_sda_o, 123 | sda_t=s2_sda_t, 124 | abw=2, 125 | address=0x51, 126 | latency=0, 127 | name='slave2' 128 | ) 129 | 130 | # WB RAM model 131 | wb_ram_inst = wb.WBRam(2**16) 132 | 133 | wb_ram_port0 = wb_ram_inst.create_port( 134 | clk, 135 | adr_i=wb_adr_o, 136 | dat_i=wb_dat_o, 137 | dat_o=wb_dat_i, 138 | we_i=wb_we_o, 139 | sel_i=wb_sel_o, 140 | stb_i=wb_stb_o, 141 | ack_o=wb_ack_i, 142 | cyc_i=wb_cyc_o, 143 | latency=1, 144 | asynchronous=False, 145 | name='port0' 146 | ) 147 | 148 | # DUT 149 | if os.system(build_cmd): 150 | raise Exception("Error running build command") 151 | 152 | dut = Cosimulation( 153 | "vvp -m myhdl %s.vvp -lxt2" % testbench, 154 | clk=clk, 155 | rst=rst, 156 | current_test=current_test, 157 | i2c_scl_i=i2c_scl_i, 158 | i2c_scl_o=i2c_scl_o, 159 | i2c_scl_t=i2c_scl_t, 160 | i2c_sda_i=i2c_sda_i, 161 | i2c_sda_o=i2c_sda_o, 162 | i2c_sda_t=i2c_sda_t, 163 | wb_adr_o=wb_adr_o, 164 | wb_dat_i=wb_dat_i, 165 | wb_dat_o=wb_dat_o, 166 | wb_we_o=wb_we_o, 167 | wb_sel_o=wb_sel_o, 168 | wb_stb_o=wb_stb_o, 169 | wb_ack_i=wb_ack_i, 170 | wb_err_i=wb_err_i, 171 | wb_cyc_o=wb_cyc_o, 172 | busy=busy, 173 | bus_addressed=bus_addressed, 174 | bus_active=bus_active, 175 | enable=enable, 176 | device_address=device_address 177 | ) 178 | 179 | @always_comb 180 | def bus(): 181 | # emulate I2C wired AND 182 | scl = m_scl_o & i2c_scl_o & s2_scl_o 183 | sda = m_sda_o & i2c_sda_o & s2_sda_o 184 | 185 | m_scl_i.next = scl; 186 | m_sda_i.next = sda; 187 | 188 | i2c_scl_i.next = scl 189 | i2c_sda_i.next = sda 190 | 191 | s2_scl_i.next = scl 192 | s2_sda_i.next = sda 193 | 194 | @always(delay(4)) 195 | def clkgen(): 196 | clk.next = not clk 197 | 198 | @instance 199 | def check(): 200 | yield delay(100) 201 | yield clk.posedge 202 | rst.next = 1 203 | yield clk.posedge 204 | rst.next = 0 205 | yield clk.posedge 206 | yield delay(100) 207 | yield clk.posedge 208 | 209 | # testbench stimulus 210 | 211 | enable.next = 1 212 | device_address.next = 0x50 213 | 214 | yield clk.posedge 215 | print("test 1: write") 216 | current_test.next = 1 217 | 218 | i2c_master_inst.init_write(0x50, b'\x00\x04'+b'\x11\x22\x33\x44') 219 | 220 | yield i2c_master_inst.wait() 221 | yield clk.posedge 222 | 223 | while busy: 224 | yield clk.posedge 225 | 226 | data = wb_ram_inst.read_mem(0, 32) 227 | for i in range(0, len(data), 16): 228 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 229 | 230 | assert wb_ram_inst.read_mem(4,4) == b'\x11\x22\x33\x44' 231 | 232 | yield delay(100) 233 | 234 | yield clk.posedge 235 | print("test 2: read") 236 | current_test.next = 2 237 | 238 | i2c_master_inst.init_write(0x50, b'\x00\x04') 239 | i2c_master_inst.init_read(0x50, 4) 240 | 241 | yield i2c_master_inst.wait() 242 | yield clk.posedge 243 | 244 | data = i2c_master_inst.get_read_data() 245 | assert data[0] == 0x50 246 | assert data[1] == b'\x11\x22\x33\x44' 247 | 248 | yield delay(100) 249 | 250 | yield clk.posedge 251 | print("test 3: various writes") 252 | current_test.next = 3 253 | 254 | for length in range(1,9): 255 | for offset in range(4): 256 | i2c_master_inst.init_write(0x50, bytearray(struct.pack('>H', 256*(16*offset+length)+offset)+b'\x11\x22\x33\x44\x55\x66\x77\x88'[0:length])) 257 | 258 | yield i2c_master_inst.wait() 259 | yield clk.posedge 260 | 261 | while busy: 262 | yield clk.posedge 263 | 264 | data = wb_ram_inst.read_mem(256*(16*offset+length), 32) 265 | for i in range(0, len(data), 16): 266 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 267 | 268 | assert wb_ram_inst.read_mem(256*(16*offset+length)+offset,length) == b'\x11\x22\x33\x44\x55\x66\x77\x88'[0:length] 269 | 270 | yield delay(100) 271 | 272 | yield clk.posedge 273 | print("test 4: various reads") 274 | current_test.next = 4 275 | 276 | for length in range(1,9): 277 | for offset in range(4): 278 | i2c_master_inst.init_write(0x50, bytearray(struct.pack('>H', 256*(16*offset+length)+offset))) 279 | i2c_master_inst.init_read(0x50, length) 280 | 281 | yield i2c_master_inst.wait() 282 | yield clk.posedge 283 | 284 | data = i2c_master_inst.get_read_data() 285 | assert data[0] == 0x50 286 | assert data[1] == b'\x11\x22\x33\x44\x55\x66\x77\x88'[0:length] 287 | 288 | yield delay(100) 289 | 290 | # TODO various reads and writes 291 | 292 | # yield clk.posedge 293 | # print("test 3: read with delays") 294 | # current_test.next = 3 295 | 296 | # i2c_master_inst.init_write(0x50, b'\x00\x04') 297 | # i2c_master_inst.init_read(0x50, 4) 298 | 299 | # data_source.send(b'\x11\x22\x33\x44') 300 | 301 | # data_source_pause.next = True 302 | # data_sink_pause.next = True 303 | 304 | # yield delay(5000) 305 | # data_sink_pause.next = False 306 | 307 | # yield delay(2000) 308 | # data_source_pause.next = False 309 | 310 | # yield i2c_master_inst.wait() 311 | # yield clk.posedge 312 | 313 | # data = None 314 | # while not data: 315 | # yield clk.posedge 316 | # data = data_sink.recv() 317 | 318 | # assert data.data == b'\x00\x04' 319 | 320 | # data = i2c_master_inst.get_read_data() 321 | # assert data[0] == 0x50 322 | # assert data[1] == b'\x11\x22\x33\x44' 323 | 324 | # yield delay(100) 325 | 326 | yield clk.posedge 327 | print("test 4: access slave 2") 328 | current_test.next = 4 329 | 330 | i2c_master_inst.init_write(0x51, b'\x00\x04'+b'\x11\x22\x33\x44') 331 | 332 | yield i2c_master_inst.wait() 333 | yield clk.posedge 334 | 335 | data = i2c_mem_inst2.read_mem(0, 32) 336 | for i in range(0, len(data), 16): 337 | print(" ".join(("{:02x}".format(c) for c in bytearray(data[i:i+16])))) 338 | 339 | assert i2c_mem_inst2.read_mem(4,4) == b'\x11\x22\x33\x44' 340 | 341 | i2c_master_inst.init_write(0x51, b'\x00\x04') 342 | i2c_master_inst.init_read(0x51, 4) 343 | 344 | yield i2c_master_inst.wait() 345 | yield clk.posedge 346 | 347 | data = i2c_master_inst.get_read_data() 348 | assert data[0] == 0x51 349 | assert data[1] == b'\x11\x22\x33\x44' 350 | 351 | yield delay(100) 352 | 353 | raise StopSimulation 354 | 355 | return instances() 356 | 357 | def test_bench(): 358 | sim = Simulation(bench()) 359 | sim.run() 360 | 361 | if __name__ == '__main__': 362 | print("Running test...") 363 | test_bench() 364 | -------------------------------------------------------------------------------- /tb/test_i2c_slave_wbm.v: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2017 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | // Language: Verilog 2001 26 | 27 | `timescale 1ns / 1ps 28 | 29 | /* 30 | * Testbench for i2c_slave_wbm 31 | */ 32 | module test_i2c_slave_wbm; 33 | 34 | // Parameters 35 | parameter FILTER_LEN = 4; 36 | parameter WB_DATA_WIDTH = 32; 37 | parameter WB_ADDR_WIDTH = 16; 38 | parameter WB_SELECT_WIDTH = WB_DATA_WIDTH/8; 39 | 40 | // Inputs 41 | reg clk = 0; 42 | reg rst = 0; 43 | reg [7:0] current_test = 0; 44 | 45 | reg i2c_scl_i = 1; 46 | reg i2c_sda_i = 1; 47 | reg [WB_DATA_WIDTH-1:0] wb_dat_i = 0; 48 | reg wb_ack_i = 0; 49 | reg wb_err_i = 0; 50 | reg enable = 0; 51 | reg [6:0] device_address = 0; 52 | 53 | // Outputs 54 | wire i2c_scl_o; 55 | wire i2c_scl_t; 56 | wire i2c_sda_o; 57 | wire i2c_sda_t; 58 | wire [WB_ADDR_WIDTH-1:0] wb_adr_o; 59 | wire [WB_DATA_WIDTH-1:0] wb_dat_o; 60 | wire wb_we_o; 61 | wire [WB_SELECT_WIDTH-1:0] wb_sel_o; 62 | wire wb_stb_o; 63 | wire wb_cyc_o; 64 | wire busy; 65 | wire bus_addressed; 66 | wire bus_active; 67 | 68 | initial begin 69 | // myhdl integration 70 | $from_myhdl( 71 | clk, 72 | rst, 73 | current_test, 74 | i2c_scl_i, 75 | i2c_sda_i, 76 | wb_dat_i, 77 | wb_ack_i, 78 | wb_err_i, 79 | enable, 80 | device_address 81 | ); 82 | $to_myhdl( 83 | i2c_scl_o, 84 | i2c_scl_t, 85 | i2c_sda_o, 86 | i2c_sda_t, 87 | wb_adr_o, 88 | wb_dat_o, 89 | wb_we_o, 90 | wb_sel_o, 91 | wb_stb_o, 92 | wb_cyc_o, 93 | busy, 94 | bus_addressed, 95 | bus_active 96 | ); 97 | 98 | // dump file 99 | $dumpfile("test_i2c_slave_wbm.lxt"); 100 | $dumpvars(0, test_i2c_slave_wbm); 101 | end 102 | 103 | i2c_slave_wbm #( 104 | .FILTER_LEN(FILTER_LEN), 105 | .WB_DATA_WIDTH(WB_DATA_WIDTH), 106 | .WB_ADDR_WIDTH(WB_ADDR_WIDTH), 107 | .WB_SELECT_WIDTH(WB_SELECT_WIDTH) 108 | ) 109 | UUT ( 110 | .clk(clk), 111 | .rst(rst), 112 | .i2c_scl_i(i2c_scl_i), 113 | .i2c_scl_o(i2c_scl_o), 114 | .i2c_scl_t(i2c_scl_t), 115 | .i2c_sda_i(i2c_sda_i), 116 | .i2c_sda_o(i2c_sda_o), 117 | .i2c_sda_t(i2c_sda_t), 118 | .wb_adr_o(wb_adr_o), 119 | .wb_dat_i(wb_dat_i), 120 | .wb_dat_o(wb_dat_o), 121 | .wb_we_o(wb_we_o), 122 | .wb_sel_o(wb_sel_o), 123 | .wb_stb_o(wb_stb_o), 124 | .wb_ack_i(wb_ack_i), 125 | .wb_err_i(wb_err_i), 126 | .wb_cyc_o(wb_cyc_o), 127 | .busy(busy), 128 | .bus_addressed(bus_addressed), 129 | .bus_active(bus_active), 130 | .enable(enable), 131 | .device_address(device_address) 132 | ); 133 | 134 | endmodule 135 | -------------------------------------------------------------------------------- /tb/wb.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (c) 2015-2016 Alex Forencich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | """ 24 | 25 | from myhdl import * 26 | import mmap 27 | 28 | class WBMaster(object): 29 | def __init__(self): 30 | self.command_queue = [] 31 | self.read_data_queue = [] 32 | self.has_logic = False 33 | self.clk = None 34 | self.cyc_o = None 35 | 36 | def init_read(self, address, length): 37 | self.command_queue.append(('r', address, length)) 38 | 39 | def init_read_words(self, address, length, ws=2): 40 | assert ws in (1, 2, 4, 8) 41 | self.init_read(int(address*ws), int(length*ws)) 42 | 43 | def init_read_dwords(self, address, length): 44 | self.init_read_words(address, length, 4) 45 | 46 | def init_read_qwords(self, address, length): 47 | self.init_read_words(address, length, 8) 48 | 49 | def init_write(self, address, data): 50 | self.command_queue.append(('w', address, data)) 51 | 52 | def init_write_words(self, address, data, ws=2): 53 | assert ws in (1, 2, 4, 8) 54 | data2 = [] 55 | for w in data: 56 | d = [] 57 | for j in range(ws): 58 | d.append(w&0xff) 59 | w >>= 8 60 | data2.extend(bytearray(d)) 61 | self.init_write(int(address*ws), data2) 62 | 63 | def init_write_dwords(self, address, data): 64 | self.init_write_words(address, data, 4) 65 | 66 | def init_write_qwords(self, address, data): 67 | self.init_write_words(address, data, 8) 68 | 69 | def idle(self): 70 | return len(self.command_queue) == 0 and not self.cyc_o.next 71 | 72 | def wait(self): 73 | while not self.idle(): 74 | yield self.clk.posedge 75 | 76 | def read_data_ready(self): 77 | return not self.read_data_queue 78 | 79 | def get_read_data(self): 80 | return self.read_data_queue.pop(0) 81 | 82 | def get_read_data_words(self, ws=2): 83 | assert ws in (1, 2, 4, 8) 84 | v = self.get_read_data() 85 | if v is None: 86 | return None 87 | address, data = v 88 | d = [] 89 | for i in range(int(len(data)/ws)): 90 | w = 0 91 | for j in range(ws-1,-1,-1): 92 | w <<= 8 93 | w += data[i*ws+j] 94 | d.append(w) 95 | return (int(address/ws), d) 96 | 97 | def get_read_data_dwords(self): 98 | return self.get_read_data_words(4) 99 | 100 | def get_read_data_qwords(self): 101 | return self.get_read_data_words(8) 102 | 103 | def create_logic(self, 104 | clk, 105 | adr_o=Signal(intbv(0)[8:]), 106 | dat_i=None, 107 | dat_o=None, 108 | we_o=Signal(bool(0)), 109 | sel_o=Signal(intbv(1)[1:]), 110 | stb_o=Signal(bool(0)), 111 | ack_i=Signal(bool(0)), 112 | cyc_o=Signal(bool(0)), 113 | name=None 114 | ): 115 | 116 | if self.has_logic: 117 | raise Exception("Logic already instantiated!") 118 | 119 | if dat_i is not None: 120 | assert len(dat_i) % 8 == 0 121 | w = len(dat_i) 122 | if dat_o is not None: 123 | assert len(dat_o) % 8 == 0 124 | w = len(dat_o) 125 | if dat_i is not None and dat_o is not None: 126 | assert len(dat_i) == len(dat_o) 127 | 128 | bw = int(w/8) # width of bus in bytes 129 | ww = len(sel_o) # width of bus in words 130 | ws = int(bw/ww) # word size in bytes 131 | 132 | assert ww in (1, 2, 4, 8) 133 | assert ws in (1, 2, 4, 8) 134 | 135 | self.has_logic = True 136 | self.clk = clk 137 | self.cyc_o = cyc_o 138 | 139 | @instance 140 | def logic(): 141 | while True: 142 | yield clk.posedge 143 | 144 | # check for commands 145 | if len(self.command_queue) > 0: 146 | cmd = self.command_queue.pop(0) 147 | 148 | # address 149 | addr = cmd[1] 150 | # address in words 151 | adw = int(addr/ws) 152 | # select for first access 153 | sel_start = ((2**(ww)-1) << int(adw % ww)) & (2**(ww)-1) 154 | 155 | if cmd[0] == 'w': 156 | data = cmd[2] 157 | # select for last access 158 | sel_end = (2**(ww)-1) >> int(ww - (((int((addr+len(data)-1)/ws)) % ww) + 1)) 159 | # number of cycles 160 | cycles = int((len(data) + bw-1 + (addr % bw)) / bw) 161 | i = 0 162 | 163 | if name is not None: 164 | print("[%s] Write data a:0x%08x d:%s" % (name, addr, " ".join(("{:02x}".format(c) for c in bytearray(data))))) 165 | 166 | cyc_o.next = 1 167 | 168 | # first cycle 169 | stb_o.next = 1 170 | we_o.next = 1 171 | adr_o.next = int(adw/ww)*ww 172 | val = 0 173 | for j in range(bw): 174 | if j >= addr % bw and (cycles > 1 or j < (((addr + len(data) - 1) % bw) + 1)): 175 | val |= bytearray(data)[i] << j*8 176 | i += 1 177 | dat_o.next = val 178 | 179 | if cycles == 1: 180 | sel_o.next = sel_start & sel_end 181 | else: 182 | sel_o.next = sel_start 183 | 184 | yield clk.posedge 185 | while not int(ack_i): 186 | yield clk.posedge 187 | 188 | stb_o.next = 0 189 | we_o.next = 0 190 | 191 | for k in range(1, cycles-1): 192 | # middle cycles 193 | yield clk.posedge 194 | stb_o.next = 1 195 | we_o.next = 1 196 | adr_o.next = int(adw/ww)*ww + k * ww 197 | val = 0 198 | for j in range(bw): 199 | val |= bytearray(data)[i] << j*8 200 | i += 1 201 | dat_o.next = val 202 | sel_o.next = 2**(ww)-1 203 | 204 | yield clk.posedge 205 | while not int(ack_i): 206 | yield clk.posedge 207 | 208 | stb_o.next = 0 209 | we_o.next = 0 210 | 211 | if cycles > 1: 212 | # last cycle 213 | yield clk.posedge 214 | stb_o.next = 1 215 | we_o.next = 1 216 | adr_o.next = int(adw/ww)*ww + (cycles-1) * ww 217 | val = 0 218 | for j in range(bw): 219 | if j < (((addr + len(data) - 1) % bw) + 1): 220 | val |= bytearray(data)[i] << j*8 221 | i += 1 222 | dat_o.next = val 223 | sel_o.next = sel_end 224 | 225 | yield clk.posedge 226 | while not int(ack_i): 227 | yield clk.posedge 228 | 229 | stb_o.next = 0 230 | we_o.next = 0 231 | 232 | we_o.next = 0 233 | stb_o.next = 0 234 | 235 | cyc_o.next = 0 236 | 237 | elif cmd[0] == 'r': 238 | length = cmd[2] 239 | data = b'' 240 | # select for last access 241 | sel_end = (2**(ww)-1) >> int(ww - (((int((addr+length-1)/ws)) % ww) + 1)) 242 | # number of cycles 243 | cycles = int((length + bw-1 + (addr % bw)) / bw) 244 | 245 | cyc_o.next = 1 246 | 247 | # first cycle 248 | stb_o.next = 1 249 | adr_o.next = int(adw/ww)*ww 250 | if cycles == 1: 251 | sel_o.next = sel_start & sel_end 252 | else: 253 | sel_o.next = sel_start 254 | 255 | yield clk.posedge 256 | while not int(ack_i): 257 | yield clk.posedge 258 | 259 | stb_o.next = 0 260 | 261 | val = int(dat_i) 262 | 263 | for j in range(bw): 264 | if j >= addr % bw and (cycles > 1 or j < (((addr + length - 1) % bw) + 1)): 265 | data += bytes(bytearray([(val >> j*8) & 255])) 266 | 267 | for k in range(1, cycles-1): 268 | # middle cycles 269 | yield clk.posedge 270 | stb_o.next = 1 271 | adr_o.next = int(adw/ww)*ww + k * ww 272 | sel_o.next = 2**(ww)-1 273 | 274 | yield clk.posedge 275 | while not int(ack_i): 276 | yield clk.posedge 277 | 278 | stb_o.next = 0 279 | 280 | val = int(dat_i) 281 | 282 | for j in range(bw): 283 | data += bytes(bytearray([(val >> j*8) & 255])) 284 | 285 | if cycles > 1: 286 | # last cycle 287 | yield clk.posedge 288 | stb_o.next = 1 289 | adr_o.next = int(adw/ww)*ww + (cycles-1) * ww 290 | sel_o.next = sel_end 291 | 292 | yield clk.posedge 293 | while not int(ack_i): 294 | yield clk.posedge 295 | 296 | stb_o.next = 0 297 | 298 | val = int(dat_i) 299 | 300 | for j in range(bw): 301 | if j < (((addr + length - 1) % bw) + 1): 302 | data += bytes(bytearray([(val >> j*8) & 255])) 303 | 304 | stb_o.next = 0 305 | cyc_o.next = 0 306 | 307 | if name is not None: 308 | print("[%s] Read data a:0x%08x d:%s" % (name, addr, " ".join(("{:02x}".format(c) for c in bytearray(data))))) 309 | 310 | self.read_data_queue.append((addr, data)) 311 | 312 | return instances() 313 | 314 | 315 | class WBRam(object): 316 | def __init__(self, size = 1024): 317 | self.size = size 318 | self.mem = mmap.mmap(-1, size) 319 | 320 | def read_mem(self, address, length): 321 | self.mem.seek(address) 322 | return self.mem.read(length) 323 | 324 | def write_mem(self, address, data): 325 | self.mem.seek(address) 326 | self.mem.write(data) 327 | 328 | def read_words(self, address, length, ws=2): 329 | assert ws in (1, 2, 4, 8) 330 | self.mem.seek(int(address*ws)) 331 | d = [] 332 | for i in range(length): 333 | w = 0 334 | data = bytearray(self.mem.read(ws)) 335 | for j in range(ws-1,-1,-1): 336 | w <<= 8 337 | w += data[j] 338 | d.append(w) 339 | return d 340 | 341 | def read_dwords(self, address, length): 342 | return self.read_words(address, length, 4) 343 | 344 | def read_qwords(self, address, length): 345 | return self.read_words(address, length, 8) 346 | 347 | def write_words(self, address, data, ws=2): 348 | assert ws in (1, 2, 4, 8) 349 | self.mem.seek(int(address*ws)) 350 | for w in data: 351 | d = [] 352 | for j in range(ws): 353 | d.append(w&0xff) 354 | w >>= 8 355 | self.mem.write(bytearray(d)) 356 | 357 | def write_dwords(self, address, length): 358 | return self.write_words(address, length, 4) 359 | 360 | def write_qwords(self, address, length): 361 | return self.write_words(address, length, 8) 362 | 363 | def create_port(self, 364 | clk, 365 | adr_i=Signal(intbv(0)[8:]), 366 | dat_i=None, 367 | dat_o=None, 368 | we_i=Signal(bool(0)), 369 | sel_i=Signal(intbv(1)[1:]), 370 | stb_i=Signal(bool(0)), 371 | ack_o=Signal(bool(0)), 372 | cyc_i=Signal(bool(0)), 373 | latency=1, 374 | asynchronous=False, 375 | name=None 376 | ): 377 | 378 | if dat_i is not None: 379 | assert len(dat_i) % 8 == 0 380 | w = len(dat_i) 381 | if dat_o is not None: 382 | assert len(dat_o) % 8 == 0 383 | w = len(dat_o) 384 | if dat_i is not None and dat_o is not None: 385 | assert len(dat_i) == len(dat_o) 386 | 387 | bw = int(w/8) # width of bus in bytes 388 | ww = len(sel_i) # width of bus in words 389 | ws = int(bw/ww) # word size in bytes 390 | 391 | assert ww in (1, 2, 4, 8) 392 | assert ws in (1, 2, 4, 8) 393 | 394 | @instance 395 | def logic(): 396 | while True: 397 | if asynchronous: 398 | yield adr_i, cyc_i, stb_i 399 | else: 400 | yield clk.posedge 401 | 402 | ack_o.next = False 403 | 404 | # address in increments of bus word width 405 | addr = int(int(adr_i)/ww)*ww 406 | 407 | if cyc_i & stb_i & ~ack_o: 408 | if asynchronous: 409 | yield delay(latency) 410 | else: 411 | for i in range(latency): 412 | yield clk.posedge 413 | ack_o.next = True 414 | self.mem.seek(addr*ws % self.size) 415 | if we_i: 416 | # write 417 | data = [] 418 | val = int(dat_i) 419 | for i in range(bw): 420 | data += [val & 0xff] 421 | val >>= 8 422 | data = bytearray(data) 423 | for i in range(ww): 424 | if sel_i & (1 << i): 425 | self.mem.write(data[i*ws:(i+1)*ws]) 426 | else: 427 | self.mem.seek(ws, 1) 428 | if name is not None: 429 | print("[%s] Write word a:0x%08x sel:0x%02x d:%s" % (name, addr, sel_i, " ".join(("{:02x}".format(c) for c in bytearray(data))))) 430 | else: 431 | data = bytearray(self.mem.read(bw)) 432 | val = 0 433 | for i in range(bw-1,-1,-1): 434 | val <<= 8 435 | val += data[i] 436 | dat_o.next = val 437 | if name is not None: 438 | print("[%s] Read word a:0x%08x d:%s" % (name, addr, " ".join(("{:02x}".format(c) for c in bytearray(data))))) 439 | 440 | return instances() 441 | 442 | --------------------------------------------------------------------------------