Please forgive my lack of knowledge! I am trying to create an OLED controller. It will control a Newhaven NHD-1.5-128128UGC3 Oled which is a 218x128 colour oled. I already have a good controller for this written in C for an ATMEGA, but thought it would be a great exercise for me to brush up my verilog skills and get it working on an FPGA. The FPGA I'm using is a Lattice MACHX03 on a MachX03 starter kit. The issue I am seeing is that I'm getting on glitch^H^H^H^H^H^H logic error on my chip-select line (wire oled_cs in my verilog code) and was hoping someone can help me understand why. Below is my verilog code for the top module, which is the only thing driving oled_cs. I will try to attache a screen grab of the logic anylzer which is seeing the oled_cs glitch. The theory is that the top module will assert oled_cs (active low) and the oled_dc line, then place a byte into oled_data and send a start signal to the spi_master module, and then wait for "oled_busy" to be released before sending the next byte via spi, and so on until all the bytes in the initialisation sequence have been sent. NOTE, I'm not trying to debug the SPI master yet, it's oled_busy signal can also be seen in the logic analyzer output, and that's all I need for now. The big issue is that the oled_cs signal seems to be released (logic high) after 2 clocks and I can't see why! I'm just learning verilog, so you might need to be patient with me. Here is the top module code, which is the only thing that drives oled_cs
module oled_ctrl ( input wire rst_n, input wire miso, output wire [9:0]gnd, output wire mosi, output wire sck, output wire oled_dc, output wire oled_cs, output wire oled_rst, output led_d2, output led_d3, output led_d4 ); localparam RESET_HOLD_COUNT = 16'h1000; localparam STATE_RESET = 8'h01; localparam STATE_SET_PINS = 8'h02; localparam STATE_START_SPI = 8'h04; localparam STATE_WAIT_SPI = 8'h08; localparam STATE_SPI_DONE = 8'h10; localparam STATE_HOLD = 8'h20; wire clk; wire oled_busy; reg [7:0] LUT_INDEX = 8'd0; reg [15:0] LUT_DATA; reg spim_start; reg [7:0] oled_data; reg [5:0] machine_state = STATE_RESET; reg [15:0] loop_counter = 16'd0; reg o_rst = 1'b1; reg o_cs = 1'b1; reg o_dc = 1'b1; assign gnd = 10'd0; assign oled_rst = o_rst; assign oled_cs = o_cs; assign oled_dc = oled_busy; // Tell-tale LEDS assign led_d2 = rst_n; assign led_d3 = ~sck; assign led_d4 = ~mosi; always @(posedge clk) begin if(!rst_n) begin oled_data <= 8'h00; spim_start <= 1'b0; machine_state <= STATE_RESET; loop_counter <= 16'h00; LUT_INDEX <= 0; end else begin // state machine case (machine_state) STATE_RESET: begin if(loop_counter==16'd0) begin o_rst <= 1'b0; // assert oled reset o_cs <= 1'b1; o_dc <= 1'b1; loop_counter <= loop_counter + 16'd1; end else if(loop_counter == RESET_HOLD_COUNT) begin o_rst <= 1'b1; // release oled reset machine_state <= STATE_SET_PINS; end else begin loop_counter <= loop_counter + 16'd1; end end STATE_SET_PINS: begin o_cs <= 1'b0; // assert oled chip select o_dc <= LUT_DATA; // command or data pin (0:CMD, 1:DATA) oled_data <= LUT_DATA[7:0]; machine_state <= STATE_START_SPI; end STATE_START_SPI: begin spim_start <= 1'b1; o_cs <= 1'b0; machine_state <= STATE_WAIT_SPI; end STATE_WAIT_SPI: begin spim_start <= 0; // release spim start o_cs <= 1'b0; if(!oled_busy) begin LUT_INDEX <= LUT_INDEX + 1'd1; machine_state <= STATE_SPI_DONE; end end STATE_SPI_DONE: begin o_cs <= 1'b1; spim_start <= 0; if(LUT_INDEX <= 8'd42) begin machine_state <= STATE_SET_PINS; end else begin machine_state <= STATE_HOLD; end end STATE_HOLD: o_cs <= 1'b1; default: o_cs <= 1'b1; endcase end end always @(posedge clk) begin case(LUT_INDEX) // Initialisation sequence for 128x128 OLED module 0: LUT_DATA <= 16'h00fd; // CMD lock =| 1: LUT_DATA <= 16'h0112; // Lock | 2: LUT_DATA <= 16'h00fd; // CMD lock | 3: LUT_DATA <= 16'h01b1; // Unlock =| Unlock sequence 4: LUT_DATA <= 16'h00AE; // Display off, sleep on 5: LUT_DATA <= 16'h00B3; // CMD DCLK Divide Ratio 6: LUT_DATA <= 16'h01f1; // clock = diviser+1 7: LUT_DATA <= 16'h00CA; // CMD Set MUX ratio 8: LUT_DATA <= 16'h017f; // OLED_END + 1 9: LUT_DATA <= 16'h00a2; // CMD Display offset 10: LUT_DATA <= 16'h0100; // 00 11: LUT_DATA <= 16'h00a1; // Display start line 12: LUT_DATA <= 16'h0100; 13: LUT_DATA <= 16'h00A0; // Set re-map, colour depth 14: LUT_DATA <= 16'h0162; // 8-Bit, 256 colours 15: LUT_DATA <= 16'h00B5; // setGPIO 16: LUT_DATA <= 16'h0100; // Disabled 17: LUT_DATA <= 16'h00AB; // Function set 18: LUT_DATA <= 16'h0101; // 8-Bit interface, internal VDD regulator 19: LUT_DATA <= 16'h00b4; // Set VSL (voltage-segment-low) 20: LUT_DATA <= 16'h01A0; // External VSL 21: LUT_DATA <= 16'h01B5; 22: LUT_DATA <= 16'h0155; 23: LUT_DATA <= 16'h00C1; // Set Contrast current for A,B,C 24: LUT_DATA <= 16'h018A; // A 25: LUT_DATA <= 16'h018a; // B 26: LUT_DATA <= 16'h018A; // C 27: LUT_DATA <= 16'h00c7; // Set mster Contrast 28: LUT_DATA <= 16'h010f; 29: LUT_DATA <= 16'h00B9; // reset to default LUT 30: LUT_DATA <= 16'h00B1; // Set pre and discharge phase length 31: LUT_DATA <= 16'h0132; // 32: LUT_DATA <= 16'h00BB; // Set pre-charge voltage of colour A,B,C 33: LUT_DATA <= 16'h0107; 34: LUT_DATA <= 16'h00B2; // Set enhanced driving scheme 35: LUT_DATA <= 16'h01a4; // 36: LUT_DATA <= 16'h0100; 37: LUT_DATA <= 16'h0100; 38: LUT_DATA <= 16'h00B6; // second precharge period 39: LUT_DATA <= 16'h0101; 40: LUT_DATA <= 16'h00BE; // Set VCOMH Voltage 41: LUT_DATA <= 16'h0107; 42: LUT_DATA <= 16'h00a6; // Normal display mode. default: LUT_DATA <= 16'h0100; endcase end /*************************/ /* Instances of modules. */ /*************************/ // Lattice OSCH Clock Module defparam OSCH_inst.NOM_FREQ = "2.08"; OSCH OSCH_inst (.STDBY(1'b0), .OSC(clk), .SEDSTDBY() ); spi_master spim(.clk(clk), .rst_n(rst_n), .miso(miso), .mosi(mosi), .sck(sck), .start(spim_start), .data_in(oled_data), .data_out(), .busy(oled_busy), .new_data()) ; endmodule
: Edited by User
Kenny M. wrote: > the oled_cs glitch That for sure is not a glitch. A glitch is something that occurs due to propagation delays in combinatorial logik. What we se here is a fairly stable and synchronous state. and fairly funny: its always on the first transmitted bit of a SPI transmission. Did you do a functional simulation of your code? Thats the very best way to ensure that the module is fit to be used. And there you can easily see the current state of each FSM...
: Edited by Moderator
>That for sure is not a glitch. Sorry. I used the wrong term, but I thought the meaning was clear. I will try to be more precise in future. I have tried to simulate this. I am using Lattice Diamond 3.7 (64 Bit) on Windows 10. I run the simulation wizard and import the code, and it creates a simulation and waveform that extends to 1us. For the life of me I can't figure out how to expand it to cover the areas I need. I guess I need to go read up on the simulator and how it works. **EDIT** Changed 100ns to 1us
: Edited by User
I persevered with the simulator, and it led me to the problem, which is now solved :-) In my code there is a race condition between the spim_master module asserting the oled_busy signal and my top module checking the signal in this:
STATE_WAIT_SPI: begin spim_start <= 0; // release spim start o_cs <= 1'b0; if(!oled_busy) begin LUT_INDEX <= LUT_INDEX + 1'd1; machine_state <= STATE_SPI_DONE; end end
One solution, which worked for me, was to invert the clock going to the spim_master, which put the spim_master half a clock pulse out of phase. A better solution would be to add a state to my state machine that waits for the oled_busy to be asserted, before waiting for it to be released again.