├── .gitignore ├── .travis.yml ├── LICENSE-APACHE ├── LICENSE-MIT ├── Manifest.py ├── README.md ├── requirements.txt ├── sim ├── imx219_tb │ └── Manifest.py └── vsim.do ├── src ├── Manifest.py ├── imx219.sv └── ov5647.sv └── test ├── Manifest.py └── imx219_tb.sv /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .venv 3 | *.pin 4 | *.pof 5 | *.sof 6 | *.qpf 7 | *.qsf 8 | *.sid 9 | *.map.* 10 | *.sta.* 11 | *.fit.* 12 | bitstream.tcl 13 | bitstream 14 | incremental_db 15 | *.hdb 16 | *.cdb 17 | *.ddb 18 | *.idb 19 | *.rdb 20 | *.logdb 21 | *.qmsg 22 | *.hsd 23 | *.ammdb 24 | db 25 | *.rpt 26 | *.sld 27 | *.jdi 28 | *.done 29 | *.pow.* 30 | Makefile 31 | project 32 | project.tcl 33 | files.tcl 34 | *.vcd 35 | *.vvp 36 | run.command 37 | modelsim.ini 38 | transcript 39 | work/ 40 | *.wlf 41 | ip_cores/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: python 3 | os: linux 4 | addons: 5 | apt: 6 | update: false 7 | packages: 8 | - lib32z1 9 | - lib32stdc++6 10 | - libexpat1:i386 11 | - libc6:i386 12 | - libsm6:i386 13 | - libncurses5:i386 14 | - libx11-6:i386 15 | - zlib1g:i386 16 | - libxext6:i386 17 | - libxft2:i386 18 | 19 | install: 20 | - pip install -r requirements.txt 21 | - stat /home/travis/intelFPGA/19.1/modelsim_ase || (curl 'http://download.altera.com/akdlm/software/acdsinst/19.1std/670/ib_installers/ModelSimSetup-19.1.0.670-linux.run' -o ModelSimSetup.run && chmod +x ModelSimSetup.run && travis_wait 30 ./ModelSimSetup.run --mode unattended --accept_eula 1 && sed -i 's/linux_rh60/linux/g' /home/travis/intelFPGA/19.1/modelsim_ase/vco ) 22 | script: 23 | - export PATH=$PATH:/home/travis/intelFPGA/19.1/modelsim_ase/bin 24 | - cd ./sim/imx219_tb/ && hdlmake fetch && hdlmake && make 25 | - cd - 26 | 27 | cache: 28 | directories: 29 | - /home/travis/intelFPGA/ 30 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Sameer Puri 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Sameer Puri 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Manifest.py: -------------------------------------------------------------------------------- 1 | modules = { 2 | "local": "./src/" 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MIPI CCS v1.1 2 | 3 | [![Build Status](https://travis-ci.com/hdl-util/mipi-ccs.svg?branch=master)](https://travis-ci.com/hdl-util/mipi-ccs) 4 | 5 | ## To-do List 6 | 7 | * IMX219 Driver 8 | * [x] Power-off Mode 9 | * [x] Standby Mode 10 | * [x] Streaming Mode 11 | * [ ] Maximize possible framerates 12 | * [ ] Custom cropping 13 | * [ ] Investigate clock multipliers and maximize mipi clock 14 | * Camera control 15 | * [ ] Analog/Digital gain 16 | * [ ] Exposure (integration time) 17 | * [ ] Orientation 18 | * Synthesis & debugging 19 | 20 | ## Reference Documents 21 | 22 | These documents are not hosted here! They are available on Library Genesis and at other locations. 23 | 24 | * [MIPI CCS v1.1](https://b-ok.cc/book/5437872/02a689) 25 | * [IMX219 Datasheet](https://github.com/rellimmot/Sony-IMX219-Raspberry-Pi-V2-CMOS/blob/master/RASPBERRY%20PI%20CAMERA%20V2%20DATASHEET%20IMX219PQH5_7.0.0_Datasheet_XXX.PDF) 26 | * [OV5647 Datasheet](https://cdn.sparkfun.com/datasheets/Dev/RaspberryPi/ov5647_full.pdf) 27 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | hdlmake==3.3 2 | -------------------------------------------------------------------------------- /sim/imx219_tb/Manifest.py: -------------------------------------------------------------------------------- 1 | action = "simulation" 2 | sim_tool = "modelsim" 3 | sim_top = "imx219_tb" 4 | 5 | sim_post_cmd = "vsim -novopt -do ../vsim.do -c imx219_tb" 6 | 7 | modules = { 8 | "local" : [ "../../test/" ], 9 | } 10 | -------------------------------------------------------------------------------- /sim/vsim.do: -------------------------------------------------------------------------------- 1 | onfinish stop 2 | run -all 3 | if { [runStatus -full] == "break simulation_stop {\$finish}" } { 4 | echo Build succeeded 5 | quit -f -code 0 6 | } else { 7 | echo Build failed with status [runStatus -full] 8 | quit -f -code 1 9 | } 10 | -------------------------------------------------------------------------------- /src/Manifest.py: -------------------------------------------------------------------------------- 1 | files = [ 2 | "imx219.sv", 3 | "ov5647.sv" 4 | ] 5 | 6 | 7 | modules = { 8 | "git": [ 9 | "https://github.com/hdl-util/i2c::master", 10 | ] 11 | } 12 | fetchto = "../ip_cores" 13 | -------------------------------------------------------------------------------- /src/imx219.sv: -------------------------------------------------------------------------------- 1 | module imx219 #( 2 | parameter int INPUT_CLK_RATE, 3 | parameter int TARGET_SCL_RATE = 400000, 4 | // Some IMX219 modules have a different address, change this if yours does 5 | parameter bit [7:0] ADDRESS = 8'h20 6 | ) ( 7 | input logic clk_in, 8 | inout wire scl, 9 | inout wire sda, 10 | // 0 = Power off 11 | // 1 = Software standby 12 | // 2 = Streaming 13 | input logic [1:0] mode, 14 | 15 | // 0 = 3280x2464 16 | // 1 = 1920x1080 17 | // 2 = 1640x1232 18 | // 3 = 640x480 19 | input logic [1:0] resolution, 20 | // 0 = RAW8 21 | // 1 = RAW10 22 | input logic format, 23 | // input logic horizontal_flip, 24 | // input logic vertical_flip, 25 | // input logic [7:0] analog_gain, 26 | // input logic [15:0] digital_gain, 27 | // input logic [15:0] exposure, // aka integration time 28 | 29 | // Goes high when inputs match sensor state 30 | // Changing inputs when the sensor isn't ready could put the sensor into an unexpected state 31 | output logic ready, 32 | output logic power_enable, 33 | // IMX219 Model ID did not match 34 | output logic model_err = 1'b0, 35 | output logic nack_err = 1'b0 36 | ); 37 | 38 | logic bus_clear; 39 | 40 | logic transfer_start = 1'b0; 41 | logic transfer_continues = 1'b0; 42 | logic [7:0] address; 43 | logic [7:0] data_tx = 8'd0; 44 | 45 | logic transfer_ready; 46 | logic interrupt; 47 | logic transaction_complete; 48 | logic nack; 49 | logic [7:0] data_rx; 50 | logic address_err; 51 | 52 | i2c_master #(.INPUT_CLK_RATE(INPUT_CLK_RATE), .TARGET_SCL_RATE(TARGET_SCL_RATE)) i2c_master ( 53 | .scl(scl), 54 | .clk_in(clk_in), 55 | .bus_clear(bus_clear), 56 | .sda(sda), 57 | .address(address), 58 | .transfer_start(transfer_start), 59 | .transfer_continues(transfer_continues), 60 | .data_tx(data_tx), 61 | .transfer_ready(transfer_ready), 62 | .interrupt(interrupt), 63 | .transaction_complete(transaction_complete), 64 | .nack(nack), 65 | .data_rx(data_rx), 66 | .address_err(address_err) 67 | ); 68 | 69 | logic [15:0] MODEL_ID = 16'h0219; 70 | 71 | logic [24:0] PRE_STANDBY [0:2]; 72 | assign PRE_STANDBY = '{ 73 | {1'b1, 16'h0000, MODEL_ID[15:8]}, // Read module_model_id high 74 | {1'b1, 16'h0001, MODEL_ID[7:0]}, // Read module_model_id low 75 | // {1'b0, 16'h0100, 8'd1}, // mode_select <= streaming (forces LP-11 on standby) 76 | {1'b0, 16'h0100, 8'd0} // mode_select <= standby 77 | }; 78 | 79 | logic [24:0] PRE_STREAM [0:58]; 80 | assign PRE_STREAM = '{ 81 | {1'b0, 16'h30eb, 8'h05}, // Manufacturer access command sequence (See Section 3-4) 82 | {1'b0, 16'h30eb, 8'h0c}, 83 | {1'b0, 16'h300a, 8'hff}, 84 | {1'b0, 16'h300b, 8'hff}, 85 | {1'b0, 16'h30eb, 8'h05}, 86 | {1'b0, 16'h30eb, 8'h09}, 87 | {1'b0, 16'h0114, 8'h01}, // CSI Lane Count (2) 88 | {1'b0, 16'h0128, 8'h00}, // MIPI Global timing (auto) 89 | {1'b0, 16'h012a, 8'h18}, // External Clock Frequency MSB (24MHz) 90 | {1'b0, 16'h012b, 8'h00}, // External Clock Frequency LSB 91 | {1'b0, 16'h0160, resolution == 2'd0 ? 8'h0d : 8'h06}, // Frame length MSB (15 FPS for full-frame, 30 FPS for other resolutions) 92 | {1'b0, 16'h0161, resolution == 2'd0 ? 8'hc6 : 8'he3}, // Frame length LSB 93 | {1'b0, 16'h0162, 8'h0d}, // Pixel clocks per line MSB (3448) 94 | {1'b0, 16'h0163, 8'h78}, // Pixel clocks per line LSB 95 | {1'b0, 16'h0164, resolution == 2'd0 ? 8'h00 : resolution == 2'd1 ? 8'h02 : resolution == 2'd2 ? 8'h00: 8'h03}, // X-address start MSB 96 | {1'b0, 16'h0165, resolution == 2'd0 ? 8'h00 : resolution == 2'd1 ? 8'ha8 : resolution == 2'd2 ? 8'h00: 8'he8}, // X-address start LSB 97 | {1'b0, 16'h0166, resolution == 2'd0 ? 8'h0c : resolution == 2'd1 ? 8'h08 : resolution == 2'd2 ? 8'h0c: 8'h08}, // X-address end MSB 98 | {1'b0, 16'h0167, resolution == 2'd0 ? 8'hcf : resolution == 2'd1 ? 8'h27 : resolution == 2'd2 ? 8'hcf: 8'he7}, // X-address end LSB 99 | {1'b0, 16'h0168, resolution == 2'd0 ? 8'h00 : resolution == 2'd1 ? 8'h02 : resolution == 2'd2 ? 8'h00: 8'h02}, // Y-address start MSB 100 | {1'b0, 16'h0169, resolution == 2'd0 ? 8'h00 : resolution == 2'd1 ? 8'hb4 : resolution == 2'd2 ? 8'h00: 8'hf0}, // Y-address start LSB 101 | {1'b0, 16'h016a, resolution == 2'd0 ? 8'h09 : resolution == 2'd1 ? 8'h06 : resolution == 2'd2 ? 8'h09: 8'h06}, // Y-address end MSB 102 | {1'b0, 16'h016b, resolution == 2'd0 ? 8'h9f : resolution == 2'd1 ? 8'heb : resolution == 2'd2 ? 8'h9f: 8'haf}, // Y-address end LSB 103 | 104 | {1'b0, 16'h016c, resolution == 2'd0 ? 8'h0c : resolution == 2'd1 ? 8'h07 : resolution == 2'd2 ? 8'h06 : 8'h02}, // X-output size MSB 105 | {1'b0, 16'h016d, resolution == 2'd0 ? 8'hd0 : resolution == 2'd1 ? 8'h80 : resolution == 2'd2 ? 8'h68 : 8'h80}, // X-output size LSB 106 | {1'b0, 16'h016e, resolution == 2'd0 ? 8'h09 : resolution == 2'd1 ? 8'h04 : resolution == 2'd2 ? 8'h04 : 8'h01}, // Y-output size MSB 107 | {1'b0, 16'h016f, resolution == 2'd0 ? 8'ha0 : resolution == 2'd1 ? 8'h38 : resolution == 2'd2 ? 8'hd0 : 8'he0}, // Y-output size LSB 108 | {1'b0, 16'h0170, 8'h01}, // X odd increment 109 | {1'b0, 16'h0171, 8'h01}, // Y odd increment 110 | {1'b0, 16'h0174, resolution == 2'd0 ? 8'h00 : resolution == 2'd1 ? 8'h00 : resolution == 2'd2 ? 8'h01 : 8'h03}, // Vertical binning mode 111 | {1'b0, 16'h0175, resolution == 2'd0 ? 8'h00 : resolution == 2'd1 ? 8'h00 : resolution == 2'd2 ? 8'h01 : 8'h03}, // Horizontal binning mode 112 | {1'b0, 16'h018c, format ? 8'h0a : 8'h08}, // CSI data format MSB 113 | {1'b0, 16'h018d, format ? 8'h0a : 8'h08}, // CSI data format LSB 114 | {1'b0, 16'h0301, format ? 8'h05 : 8'h04}, // Video timing pixel clock divider (/5 for 10-bit, /4 for 8-bit) 115 | {1'b0, 16'h0303, 8'h01}, // Video timing system clock divider (always /1) 116 | {1'b0, 16'h0304, 8'h03}, // External (pre-PLL) clock divider for video timing (3 for 24MHz to 27MHz) 117 | {1'b0, 16'h0305, 8'h03}, // External (pre-PLL) clock divider for output (3 for 24MHz to 27MHz) 118 | {1'b0, 16'h0306, 8'h00}, // PLL video timing system multiplier MSB 119 | {1'b0, 16'h0307, 8'h20}, // PLL video timing system multiplier LSB 120 | {1'b0, 16'h0309, format ? 8'h0a : 8'h08}, // Output pixel clock divider (/10 for 10-bit, /8 for 8-bit) 121 | {1'b0, 16'h030b, 8'h01}, // Output sytem clock divider (always /2) 122 | {1'b0, 16'h030c, 8'h00}, // PLL output system clock multiplier MSB 123 | {1'b0, 16'h030d, 8'h40}, // PLL output system clock multiplier LSB (DDR clock, as compared to 0x0307) 124 | {1'b0, 16'h0624, resolution == 2'd0 ? 8'h0c : resolution == 2'd1 ? 8'h07 : resolution == 2'd2 ? 8'h06 : 8'h02}, // Test pattern window width MSB 125 | {1'b0, 16'h0625, resolution == 2'd0 ? 8'hd0 : resolution == 2'd1 ? 8'h80 : resolution == 2'd2 ? 8'h68 : 8'h80}, // Test pattern window width LSB 126 | {1'b0, 16'h0626, resolution == 2'd0 ? 8'h09 : resolution == 2'd1 ? 8'h04 : resolution == 2'd2 ? 8'h04 : 8'h01}, // Test pattern window height MSB 127 | {1'b0, 16'h0627, resolution == 2'd0 ? 8'ha0 : resolution == 2'd1 ? 8'h38 : resolution == 2'd2 ? 8'hd0 : 8'he0}, // Test pattern window height LSB 128 | {1'b0, 16'h455e, 8'h00}, // CMOS Image Sensor Tuning for all below 129 | {1'b0, 16'h471e, 8'h4b}, 130 | {1'b0, 16'h4767, 8'h0f}, 131 | {1'b0, 16'h4750, 8'h14}, 132 | {1'b0, 16'h4540, 8'h00}, 133 | {1'b0, 16'h47b4, 8'h14}, 134 | {1'b0, 16'h4713, 8'h30}, 135 | {1'b0, 16'h478b, 8'h10}, 136 | {1'b0, 16'h478f, 8'h10}, 137 | {1'b0, 16'h4793, 8'h10}, 138 | {1'b0, 16'h4797, 8'h0e}, 139 | {1'b0, 16'h479b, 8'h0e}, 140 | {1'b0, 16'h0100, 8'h01} // Start streaming 141 | }; 142 | 143 | logic [24:0] POST_STREAM [0:0]; 144 | assign POST_STREAM = '{ 145 | {1'b0, 16'h0100, 8'h00} // Send to standby 146 | // TODO: standby spinlock 147 | }; 148 | 149 | 150 | // 0 = Off 151 | // 1 = Pre-Standby 152 | // 2 = Standby 153 | // 3 = Pre-Stream 154 | // 4 = Stream 155 | // 5 = Modify Stream 156 | // 6 = Post Stream (shutting down) 157 | // 7 = Error 158 | logic [2:0] sensor_state = 3'd0; 159 | 160 | logic [7:0] rom_counter = 8'd0; 161 | logic [1:0] byte_counter = 2'd0; 162 | 163 | // Uninit, Standby, or Stream 164 | assign ready = sensor_state == 3'd0 || sensor_state == 3'd2 || sensor_state == 3'd4; 165 | 166 | assign power_enable = sensor_state != 3'd0; 167 | 168 | logic [7:0] rom_end; 169 | assign rom_end = sensor_state == 3'd1 ? 8'd2 : sensor_state == 3'd3 ? 8'd58 : sensor_state == 3'd6 ? 8'd0 : 8'd0; 170 | 171 | logic [24:0] current_rom; 172 | assign current_rom = sensor_state == 3'd1 ? PRE_STANDBY[rom_counter] : sensor_state == 3'd3 ? PRE_STREAM[rom_counter] : sensor_state == 3'd6 ? POST_STREAM[rom_counter] : 25'd0; 173 | 174 | always @(posedge clk_in) 175 | begin 176 | case (sensor_state) 177 | 3'd0: begin 178 | if (mode != 2'd0) 179 | sensor_state <= 3'd1; 180 | end 181 | 3'd1, 3'd3, 3'd6: begin 182 | if (interrupt || transfer_ready) 183 | begin 184 | if (interrupt && (address_err || (!address[0] && nack))) // Catch write nacks 185 | begin 186 | transfer_start <= 1'b0; 187 | transfer_continues <= 1'b0; 188 | byte_counter <= 2'd0; 189 | rom_counter <= 8'd0; 190 | nack_err <= 1'd1; 191 | sensor_state <= 3'd7; 192 | end 193 | else if (transfer_ready && byte_counter == 2'd0) // Write address MSB 194 | begin 195 | transfer_start <= 1'd1; 196 | transfer_continues <= 1'd1; 197 | address <= {ADDRESS[7:1], 1'b0}; 198 | data_tx <= current_rom[23:16]; 199 | byte_counter <= 2'd1; 200 | end 201 | else if (interrupt && byte_counter == 2'd1) // Write address LSB 202 | begin 203 | transfer_start <= 1'd0; 204 | transfer_continues <= !current_rom[24]; 205 | data_tx <= current_rom[15:8]; 206 | byte_counter <= 2'd2; 207 | end 208 | else if (interrupt && byte_counter == 2'd2) // Write/Read register 209 | begin 210 | transfer_start <= current_rom[24]; 211 | transfer_continues <= 1'd0; 212 | if (current_rom[24]) 213 | address <= {ADDRESS[7:1], 1'b1}; 214 | data_tx <= current_rom[7:0]; 215 | byte_counter <= 2'd3; 216 | end 217 | else if (interrupt && byte_counter == 2'd3) // Readback 218 | begin 219 | transfer_start <= 1'd0; 220 | transfer_continues <= 1'd0; 221 | byte_counter <= 2'd0; 222 | 223 | if (current_rom[24] && current_rom[7:0] != data_rx) // Read did not match expected 224 | begin 225 | rom_counter <= 8'd0; 226 | if (sensor_state == 3'd1) // was a model error 227 | model_err <= 1'd1; 228 | sensor_state <= 3'd7; 229 | end 230 | else if (rom_counter == rom_end) // This was the last operation 231 | begin 232 | rom_counter <= 8'd0; 233 | if (sensor_state == 3'd5) 234 | sensor_state <= 3'd4; // Modifications complete 235 | else if (sensor_state == 3'd6) 236 | sensor_state <= mode == 2'd1 ? 3'd2 : 3'd0; // Either go to standby or power off 237 | else 238 | sensor_state <= sensor_state + 1'd1; // Pre-standby and Pre-stream 239 | end 240 | else 241 | rom_counter <= rom_counter + 1'd1; 242 | end 243 | end 244 | end 245 | 3'd2: begin 246 | if (mode == 2'd0) 247 | sensor_state <= 3'd0; 248 | else if (mode == 2'd2) 249 | sensor_state <= 3'd3; 250 | else 251 | sensor_state <= 3'd2; 252 | end 253 | 3'd4: begin 254 | if (mode != 2'd2) 255 | sensor_state <= 3'd6; 256 | else 257 | sensor_state <= 3'd4; 258 | end 259 | 3'd5: begin // Not entered, modify support still WIP 260 | end 261 | 3'd7: begin 262 | if (mode == 2'd0) 263 | begin 264 | model_err <= 1'd0; 265 | nack_err <= 1'd0; 266 | sensor_state <= 3'd0; 267 | end 268 | end 269 | endcase 270 | end 271 | 272 | endmodule 273 | -------------------------------------------------------------------------------- /src/ov5647.sv: -------------------------------------------------------------------------------- 1 | module ov5647 #( 2 | parameter int INPUT_CLK_RATE, 3 | parameter int TARGET_SCL_RATE = 400000, 4 | // Some ov5647 modules have a different address, change this if yours does 5 | parameter bit [7:0] ADDRESS = 8'h6c 6 | ) ( 7 | input logic clk_in, 8 | inout wire scl, 9 | inout wire sda, 10 | // 0 = Power off 11 | // 1 = Software standby 12 | // 2 = Streaming 13 | input logic [1:0] mode, 14 | 15 | // 0 = 3280x2464 16 | // 1 = 1920x1080 17 | // 2 = 1640x1232 18 | // 3 = 640x480 19 | input logic [1:0] resolution, 20 | // 0 = RAW8 21 | // 1 = RAW10 22 | input logic format, 23 | // input logic horizontal_flip, 24 | // input logic vertical_flip, 25 | // input logic [7:0] analog_gain, 26 | // input logic [15:0] digital_gain, 27 | // input logic [15:0] exposure, // aka integration time 28 | 29 | // Goes high when inputs match sensor state 30 | // Changing inputs when the sensor isn't ready could put the sensor into an unexpected state 31 | output logic ready, 32 | output logic power_enable, 33 | // ov5647 Model ID did not match 34 | output logic model_err = 1'b0, 35 | output logic nack_err = 1'b0 36 | ); 37 | 38 | logic bus_clear; 39 | 40 | logic transfer_start = 1'b0; 41 | logic transfer_continues = 1'b0; 42 | logic [7:0] address; 43 | logic [7:0] data_tx = 8'd0; 44 | 45 | logic transfer_ready; 46 | logic interrupt; 47 | logic transaction_complete; 48 | logic nack; 49 | logic [7:0] data_rx; 50 | logic address_err; 51 | 52 | i2c_master #(.INPUT_CLK_RATE(INPUT_CLK_RATE), .TARGET_SCL_RATE(TARGET_SCL_RATE)) i2c_master ( 53 | .scl(scl), 54 | .clk_in(clk_in), 55 | .bus_clear(bus_clear), 56 | .sda(sda), 57 | .address(address), 58 | .transfer_start(transfer_start), 59 | .transfer_continues(transfer_continues), 60 | .data_tx(data_tx), 61 | .transfer_ready(transfer_ready), 62 | .interrupt(interrupt), 63 | .transaction_complete(transaction_complete), 64 | .nack(nack), 65 | .data_rx(data_rx), 66 | .address_err(address_err) 67 | ); 68 | 69 | logic [15:0] MODEL_ID = 16'h5647; 70 | 71 | logic [24:0] PRE_STANDBY [0:2]; 72 | assign PRE_STANDBY = '{ 73 | {1'b1, 16'h300a, MODEL_ID[15:8]}, // Read module_model_id high 74 | {1'b1, 16'h300b, MODEL_ID[7:0]}, // Read module_model_id low 75 | {1'b0, 16'h0100, 8'd0} // mode_select <= standby 76 | }; 77 | 78 | logic [24:0] PRE_STREAM [0:88]; 79 | assign PRE_STREAM = '{ 80 | {1'b0, 16'h3034, 8'h08}, // PLL ctrl0: mipi 10 bit mode 81 | {1'b0, 16'h3035, 8'h41}, // SC common PLL ctrl1: system_clk_div by 4, scale_divider_mipi by 1 82 | {1'b0, 16'h3036, 8'h46}, // PLL multiplier: times 70 83 | {1'b0, 16'h303c, 8'h11}, // PLLS ctrl2: plls_cp 1, plls_sys_div by 1 84 | {1'b0, 16'h3106, 8'hf5}, // SRB ctrl: pll_sclk / 4, enable sclk to arbiter 85 | {1'b0, 16'h3821, 8'h07}, // Timing TC: r_mirror_isp, r_mirror_snr, r_hbin 86 | {1'b0, 16'h3820, 8'h41}, // Timing TC: r_vbin, 1 unknown setting 87 | {1'b0, 16'h3827, 8'hec}, // Debug mode 88 | {1'b0, 16'h370c, 8'h0f}, // ??? 89 | {1'b0, 16'h3612, 8'h59}, // ??? 90 | {1'b0, 16'h3618, 8'h00}, // ??? 91 | {1'b0, 16'h5000, 8'h06}, // Black/white pixel cancellation 92 | {1'b0, 16'h5001, 8'h01}, // Auto-white balance 93 | {1'b0, 16'h5002, 8'h41}, // Auto-white balance gain, Win enable 94 | {1'b0, 16'h5003, 8'h08}, // Buffer enable 95 | {1'b0, 16'h5a00, 8'h08}, // Unused bit set, not sure why 96 | {1'b0, 16'h3000, 8'h00}, // ??? 97 | {1'b0, 16'h3001, 8'h00}, // ??? 98 | {1'b0, 16'h3002, 8'h00}, // ??? 99 | {1'b0, 16'h3016, 8'h08}, // Mipi enable 100 | {1'b0, 16'h3017, 8'he0}, // pgm_vcm = 11, pgm_lptx = 10 101 | {1'b0, 16'h3018, 8'h44}, // Mipi two lane, mipi enable 102 | {1'b0, 16'h301c, 8'hf8}, // ??? 103 | {1'b0, 16'h301d, 8'hf0}, // ??? 104 | {1'b0, 16'h3a18, 8'h00}, // aec gain ceiling = 248 105 | {1'b0, 16'h3a19, 8'hf8}, // ctd. 106 | {1'b0, 16'h3c01, 8'h80}, // 50/60 Hz detection 107 | {1'b0, 16'h3b07, 8'h0c}, // exposure time 108 | {1'b0, 16'h380c, 8'h07}, // total horizontal size = 1896 109 | {1'b0, 16'h380d, 8'h68}, // ctd. 110 | {1'b0, 16'h380e, 8'h03}, // total vertical size = 984 111 | {1'b0, 16'h380f, 8'hd8}, // ctd. 112 | {1'b0, 16'h3814, 8'h31}, // horizontal subsample odd increase number = 1, horizontal subsample even increase number = 3 113 | {1'b0, 16'h3815, 8'h31}, // vertical subsample odd increase number = 1, vertical subsample even increase number = 3 114 | {1'b0, 16'h3708, 8'h64}, // ??? 115 | {1'b0, 16'h3709, 8'h52}, // ??? 116 | {1'b0, 16'h3808, 8'h02}, // x output size = 640 117 | {1'b0, 16'h3809, 8'h80}, // ctd. 118 | {1'b0, 16'h380a, 8'h01}, // y output size = 480 119 | {1'b0, 16'h380b, 8'he0}, // ctd. 120 | {1'b0, 16'h3800, 8'h00}, // x addr start = 0 121 | {1'b0, 16'h3801, 8'h00}, // ctd. 122 | {1'b0, 16'h3802, 8'h00}, // y addr start = 0 123 | {1'b0, 16'h3803, 8'h00}, // ctd. 124 | {1'b0, 16'h3804, 8'h0a}, // x addr end = 2623 125 | {1'b0, 16'h3805, 8'h3f}, // ctd. 126 | {1'b0, 16'h3806, 8'h07}, // y addr end = 1953 127 | {1'b0, 16'h3807, 8'ha1}, // ctd. 128 | {1'b0, 16'h3811, 8'h08}, // ISP horizontal offset = 8 129 | {1'b0, 16'h3813, 8'h02}, // ISP vertical offset = 2 130 | {1'b0, 16'h3630, 8'h2e}, 131 | {1'b0, 16'h3632, 8'he2}, 132 | {1'b0, 16'h3633, 8'h23}, 133 | {1'b0, 16'h3634, 8'h44}, 134 | {1'b0, 16'h3636, 8'h06}, 135 | {1'b0, 16'h3620, 8'h64}, 136 | {1'b0, 16'h3621, 8'he0}, 137 | {1'b0, 16'h3600, 8'h37}, 138 | {1'b0, 16'h3704, 8'ha0}, 139 | {1'b0, 16'h3703, 8'h5a}, 140 | {1'b0, 16'h3715, 8'h78}, 141 | {1'b0, 16'h3717, 8'h01}, 142 | {1'b0, 16'h3731, 8'h02}, 143 | {1'b0, 16'h370b, 8'h60}, 144 | {1'b0, 16'h3705, 8'h1a}, 145 | {1'b0, 16'h3f05, 8'h02}, 146 | {1'b0, 16'h3f06, 8'h10}, 147 | {1'b0, 16'h3f01, 8'h0a}, 148 | {1'b0, 16'h3a08, 8'h01}, // b50_step = 295 149 | {1'b0, 16'h3a09, 8'h27}, // ctd. 150 | {1'b0, 16'h3a0a, 8'h00}, // b60_step = 246 151 | {1'b0, 16'h3a0b, 8'hf6}, // ctd. 152 | {1'b0, 16'h3a0d, 8'h04}, // b60_max = 4 153 | {1'b0, 16'h3a0e, 8'h03}, // b50_max = 3 154 | {1'b0, 16'h3a0f, 8'h58}, // WPT stable range high limit 155 | {1'b0, 16'h3a10, 8'h50}, // BPT stable range low limit 156 | {1'b0, 16'h3a1b, 8'h58}, // WPT2 stable range high limit 157 | {1'b0, 16'h3a1e, 8'h50}, // BPT2 stable range low limit 158 | {1'b0, 16'h3a11, 8'h60}, // High VPT 159 | {1'b0, 16'h3a1f, 8'h28}, // Low VPT 160 | {1'b0, 16'h4001, 8'h02}, // Start line = 2 161 | {1'b0, 16'h4004, 8'h02}, // blc line num = 2 162 | {1'b0, 16'h4000, 8'h09}, // adc11bit mode, blc enable 163 | {1'b0, 16'h4837, 8'h24}, // PCLK_PERIOD 164 | {1'b0, 16'h4050, 8'h6e}, // BLC max 165 | {1'b0, 16'h4051, 8'h8f}, // BLC stable range 166 | {1'b0, 16'h503d, 8'b00000000}, // test pattern control 167 | {1'b0, 16'h4800, 8'b00000100}, // MIPI ctrl 168 | {1'b0, 16'h0100, 8'h01} 169 | // {1'b0, 16'h4202, 8'h00}, 170 | // {1'b0, 16'h300d, 8'h00} 171 | }; 172 | 173 | logic [24:0] POST_STREAM [0:0]; 174 | assign POST_STREAM = '{ 175 | {1'b0, 16'h0100, 8'h00} // Send to standby 176 | // TODO: standby spinlock 177 | }; 178 | 179 | 180 | // 0 = Off 181 | // 1 = Pre-Standby 182 | // 2 = Standby 183 | // 3 = Pre-Stream 184 | // 4 = Stream 185 | // 5 = Modify Stream 186 | // 6 = Post Stream (shutting down) 187 | // 7 = Error 188 | logic [2:0] sensor_state = 3'd0; 189 | 190 | logic [7:0] rom_counter = 8'd0; 191 | logic [1:0] byte_counter = 2'd0; 192 | 193 | // Uninit, Standby, or Stream 194 | assign ready = sensor_state == 3'd0 || sensor_state == 3'd2 || sensor_state == 3'd4; 195 | 196 | assign power_enable = sensor_state != 3'd0; 197 | 198 | logic [7:0] rom_end; 199 | assign rom_end = sensor_state == 3'd1 ? 8'd2 : sensor_state == 3'd3 ? 8'd88 : sensor_state == 3'd6 ? 8'd0 : 8'd0; 200 | 201 | logic [24:0] current_rom; 202 | assign current_rom = sensor_state == 3'd1 ? PRE_STANDBY[rom_counter] : sensor_state == 3'd3 ? PRE_STREAM[rom_counter] : sensor_state == 3'd6 ? POST_STREAM[rom_counter] : 25'd0; 203 | 204 | always @(posedge clk_in) 205 | begin 206 | case (sensor_state) 207 | 3'd0: begin 208 | if (mode != 2'd0) 209 | sensor_state <= 3'd1; 210 | end 211 | 3'd1, 3'd3, 3'd6: begin 212 | if (interrupt || transfer_ready) 213 | begin 214 | if (interrupt && (address_err || (!address[0] && nack))) // Catch write nacks 215 | begin 216 | transfer_start <= 1'b0; 217 | transfer_continues <= 1'b0; 218 | byte_counter <= 2'd0; 219 | rom_counter <= 8'd0; 220 | nack_err <= 1'd1; 221 | sensor_state <= 3'd7; 222 | end 223 | else if (transfer_ready && byte_counter == 2'd0) // Write address MSB 224 | begin 225 | transfer_start <= 1'd1; 226 | transfer_continues <= 1'd1; 227 | address <= {ADDRESS[7:1], 1'b0}; 228 | data_tx <= current_rom[23:16]; 229 | byte_counter <= 2'd1; 230 | end 231 | else if (interrupt && byte_counter == 2'd1) // Write address LSB 232 | begin 233 | transfer_start <= 1'd0; 234 | transfer_continues <= !current_rom[24]; 235 | data_tx <= current_rom[15:8]; 236 | byte_counter <= 2'd2; 237 | end 238 | else if (interrupt && byte_counter == 2'd2) // Write/Read register 239 | begin 240 | transfer_start <= current_rom[24]; 241 | transfer_continues <= 1'd0; 242 | if (current_rom[24]) 243 | address <= {ADDRESS[7:1], 1'b1}; 244 | data_tx <= current_rom[7:0]; 245 | byte_counter <= 2'd3; 246 | end 247 | else if (interrupt && byte_counter == 2'd3) // Readback 248 | begin 249 | transfer_start <= 1'd0; 250 | transfer_continues <= 1'd0; 251 | byte_counter <= 2'd0; 252 | 253 | if (current_rom[24] && current_rom[7:0] != data_rx) // Read did not match expected 254 | begin 255 | rom_counter <= 8'd0; 256 | if (sensor_state == 3'd1) // was a model error 257 | model_err <= 1'd1; 258 | sensor_state <= 3'd7; 259 | end 260 | else if (rom_counter == rom_end) // This was the last operation 261 | begin 262 | rom_counter <= 8'd0; 263 | if (sensor_state == 3'd5) 264 | sensor_state <= 3'd4; // Modifications complete 265 | else if (sensor_state == 3'd6) 266 | sensor_state <= mode == 2'd1 ? 3'd2 : 3'd0; // Either go to standby or power off 267 | else 268 | sensor_state <= sensor_state + 1'd1; // Pre-standby and Pre-stream 269 | end 270 | else 271 | rom_counter <= rom_counter + 1'd1; 272 | end 273 | end 274 | end 275 | 3'd2: begin 276 | if (mode == 2'd0) 277 | sensor_state <= 3'd0; 278 | else if (mode == 2'd2) 279 | sensor_state <= 3'd3; 280 | else 281 | sensor_state <= 3'd2; 282 | end 283 | 3'd4: begin 284 | if (mode != 2'd2) 285 | sensor_state <= 3'd6; 286 | else 287 | sensor_state <= 3'd4; 288 | end 289 | 3'd5: begin // Not entered, modify support still WIP 290 | end 291 | 3'd7: begin 292 | if (mode == 2'd0) 293 | begin 294 | model_err <= 1'd0; 295 | nack_err <= 1'd0; 296 | sensor_state <= 3'd0; 297 | end 298 | end 299 | endcase 300 | end 301 | 302 | endmodule 303 | -------------------------------------------------------------------------------- /test/Manifest.py: -------------------------------------------------------------------------------- 1 | files = [ 2 | "imx219_tb.sv" 3 | ] 4 | 5 | modules = { 6 | "local" : [ "../src/" ], 7 | } 8 | -------------------------------------------------------------------------------- /test/imx219_tb.sv: -------------------------------------------------------------------------------- 1 | module imx219_tb(); 2 | 3 | // Initially, lines are grounded 4 | logic clock_p = 0; 5 | logic clock_n = 0; 6 | always 7 | begin 8 | #2ns; 9 | clock_p <= ~clock_p; 10 | clock_n <= clock_p; 11 | end 12 | 13 | logic [1:0] data_p = 2'd0; 14 | logic [1:0] data_n; 15 | assign data_n = ~data_p; 16 | 17 | logic clk_in; 18 | wire scl; 19 | wire sda; 20 | logic [1:0] mode; 21 | logic [1:0] resolution; 22 | logic format; 23 | logic ready; 24 | logic power_enable; 25 | logic model_err; 26 | logic nack_err; 27 | 28 | imx219 #(.INPUT_CLK_RATE(48000000)) imx219 ( 29 | .clk_in(clk_in), 30 | .scl(scl), 31 | .sda(sda), 32 | // 0 = Power off 33 | // 1 = Software standby 34 | // 2 = Streaming 35 | .mode(mode), 36 | 37 | // 0 = 3280x2464 38 | // 1 = 1920x1080 39 | // 2 = 1640x1232 40 | // 3 = 640x480 41 | .resolution(resolution), 42 | // 0 = RAW8 43 | // 1 = RAW10 44 | .format(format), 45 | 46 | // Goes high when inputs match sensor state 47 | // Changing inputs when the sensor isn't ready could put the sensor into an unexpected state 48 | .ready(ready), 49 | .power_enable(power_enable), 50 | // IMX219 Model ID did not match 51 | .model_err(model_err), 52 | .nack_err(nack_err) 53 | ); 54 | 55 | initial 56 | begin 57 | $finish; 58 | end 59 | 60 | endmodule 61 | --------------------------------------------------------------------------------