EmbDev.net

Forum: FPGA, VHDL & Verilog [newbie] chip select - unexpected result?


von Kenny M. (kennym)


Attached files:

Rate this post
useful
not useful
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
1
module oled_ctrl (
2
  input  wire rst_n,
3
  input  wire miso,
4
  output wire [9:0]gnd,
5
  output wire mosi,
6
  output wire sck,
7
  output wire oled_dc,
8
  output wire oled_cs,
9
  output wire oled_rst,
10
  output led_d2,
11
  output led_d3,
12
  output led_d4
13
);
14
15
  localparam RESET_HOLD_COUNT = 16'h1000;
16
  localparam STATE_RESET     = 8'h01;
17
  localparam STATE_SET_PINS  = 8'h02;
18
  localparam STATE_START_SPI  = 8'h04;
19
  localparam STATE_WAIT_SPI  = 8'h08;
20
  localparam STATE_SPI_DONE  = 8'h10;
21
  localparam STATE_HOLD    = 8'h20;
22
23
  wire clk;
24
  wire oled_busy;
25
  
26
  reg [7:0] LUT_INDEX = 8'd0;
27
  reg [15:0] LUT_DATA;
28
  
29
  reg spim_start;
30
  reg [7:0] oled_data;
31
  reg [5:0] machine_state = STATE_RESET;
32
  reg [15:0] loop_counter = 16'd0;
33
  
34
  reg o_rst = 1'b1;
35
  reg o_cs = 1'b1;
36
  reg o_dc = 1'b1;
37
  
38
  assign gnd = 10'd0;
39
  assign oled_rst = o_rst;
40
  assign oled_cs = o_cs;
41
  assign oled_dc = oled_busy;
42
  
43
  // Tell-tale LEDS
44
  assign led_d2 = rst_n;
45
  assign led_d3 = ~sck;
46
  assign led_d4 = ~mosi;
47
48
49
  always @(posedge clk) begin
50
    if(!rst_n) begin
51
      oled_data     <= 8'h00;
52
      spim_start     <= 1'b0;
53
      machine_state   <= STATE_RESET;
54
      loop_counter   <= 16'h00;
55
      LUT_INDEX     <= 0;
56
    end 
57
    else begin
58
      // state machine
59
      case (machine_state)
60
      STATE_RESET: begin
61
        if(loop_counter==16'd0) begin
62
          o_rst <= 1'b0; // assert oled reset
63
          o_cs <= 1'b1;
64
          o_dc <= 1'b1;
65
          loop_counter <= loop_counter + 16'd1;
66
        end
67
        else if(loop_counter == RESET_HOLD_COUNT) begin
68
          o_rst <= 1'b1; // release oled reset
69
          machine_state <= STATE_SET_PINS;
70
        end else begin
71
          loop_counter <= loop_counter + 16'd1;
72
        end
73
      end
74
      
75
      STATE_SET_PINS: begin
76
        o_cs <= 1'b0; // assert oled chip select
77
        o_dc <= LUT_DATA[8];  // command or data pin (0:CMD, 1:DATA)
78
        oled_data <= LUT_DATA[7:0];
79
        machine_state <= STATE_START_SPI;
80
      end
81
      
82
      STATE_START_SPI: begin
83
        spim_start <= 1'b1;
84
        o_cs <= 1'b0;
85
        machine_state <= STATE_WAIT_SPI;
86
      end
87
      
88
      STATE_WAIT_SPI: begin
89
        spim_start <= 0; // release spim start
90
        o_cs <= 1'b0;
91
        if(!oled_busy) begin
92
          LUT_INDEX <= LUT_INDEX + 1'd1;
93
          machine_state <= STATE_SPI_DONE;
94
        end
95
      end
96
      
97
      STATE_SPI_DONE: begin
98
        o_cs <= 1'b1;
99
        spim_start <= 0;
100
        if(LUT_INDEX <= 8'd42) begin
101
          machine_state <= STATE_SET_PINS;
102
        end else begin
103
          machine_state <= STATE_HOLD;
104
        end
105
      end
106
      
107
      STATE_HOLD:
108
        o_cs <= 1'b1;
109
      default:
110
        o_cs <= 1'b1;
111
      
112
      endcase
113
    end 
114
  end
115
  
116
117
always @(posedge clk) begin
118
  case(LUT_INDEX)
119
  // Initialisation sequence for 128x128 OLED module
120
  0:  LUT_DATA <= 16'h00fd;  // CMD lock   =|
121
  1:  LUT_DATA <= 16'h0112;  // Lock       |  
122
  2:  LUT_DATA <= 16'h00fd;  // CMD lock     |
123
  3:  LUT_DATA <= 16'h01b1;  // Unlock    =| Unlock sequence
124
  
125
  4:  LUT_DATA <= 16'h00AE;  // Display off, sleep on
126
  
127
  5:  LUT_DATA <= 16'h00B3;  // CMD DCLK Divide Ratio
128
  6:  LUT_DATA <= 16'h01f1;  // clock = diviser+1
129
  
130
  7:  LUT_DATA <= 16'h00CA;  // CMD Set MUX ratio
131
  8:  LUT_DATA <= 16'h017f;  // OLED_END + 1
132
  
133
  9:  LUT_DATA <= 16'h00a2;  // CMD Display offset
134
  10:  LUT_DATA <= 16'h0100;  //  00
135
  
136
  11:  LUT_DATA <= 16'h00a1;  // Display start line
137
  12:  LUT_DATA <= 16'h0100;
138
139
  13:  LUT_DATA <= 16'h00A0;  // Set re-map, colour depth
140
  14:  LUT_DATA <= 16'h0162;  // 8-Bit, 256 colours
141
  
142
  15:  LUT_DATA <= 16'h00B5;  // setGPIO
143
  16:  LUT_DATA <= 16'h0100;  // Disabled
144
145
  17:  LUT_DATA <= 16'h00AB;  // Function set
146
  18:  LUT_DATA <= 16'h0101;  // 8-Bit interface, internal VDD regulator
147
148
  19:  LUT_DATA <= 16'h00b4;  // Set VSL (voltage-segment-low)
149
  20:  LUT_DATA <= 16'h01A0;  //   External VSL
150
  21:  LUT_DATA <= 16'h01B5;
151
  22:  LUT_DATA <= 16'h0155;
152
153
  23:  LUT_DATA <= 16'h00C1;  // Set Contrast current for A,B,C
154
  24:  LUT_DATA <= 16'h018A;  // A
155
  25:  LUT_DATA <= 16'h018a;   // B
156
  26:  LUT_DATA <= 16'h018A;  // C
157
158
  27:  LUT_DATA <= 16'h00c7;  // Set mster Contrast
159
  28:  LUT_DATA <= 16'h010f;
160
  
161
  29:  LUT_DATA <= 16'h00B9;  // reset to default LUT
162
  
163
  30:  LUT_DATA <= 16'h00B1;  // Set pre and discharge phase length
164
  31:  LUT_DATA <= 16'h0132;  //
165
  
166
  32:  LUT_DATA <= 16'h00BB;  // Set pre-charge voltage of colour A,B,C
167
  33:  LUT_DATA <= 16'h0107;
168
  
169
  34:  LUT_DATA <= 16'h00B2;  // Set enhanced driving scheme
170
  35:  LUT_DATA <= 16'h01a4;  //
171
  36:  LUT_DATA <= 16'h0100;  
172
  37:  LUT_DATA <= 16'h0100;
173
  
174
  38:  LUT_DATA <= 16'h00B6;  // second precharge period
175
  39:  LUT_DATA <= 16'h0101;
176
  
177
  40:  LUT_DATA <= 16'h00BE;  // Set VCOMH Voltage
178
  41:  LUT_DATA <= 16'h0107;
179
  
180
  42:  LUT_DATA <= 16'h00a6;  // Normal display mode.
181
  
182
  default:  LUT_DATA  <=  16'h0100;
183
  endcase
184
end
185
186
187
  /*************************/
188
  /* Instances of modules. */
189
  /*************************/
190
  // Lattice OSCH Clock Module
191
  defparam   OSCH_inst.NOM_FREQ = "2.08";  
192
  OSCH     OSCH_inst  (.STDBY(1'b0), .OSC(clk), .SEDSTDBY() );
193
  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()) ;
194
endmodule

: Edited by User
von Lothar M. (Company: Titel) (lkmiller) (Moderator)


Rate this post
useful
not useful
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
von Kenny M. (kennym)


Rate this post
useful
not useful
>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
von Kenny M. (kennym)


Rate this post
useful
not useful
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:
1
 STATE_WAIT_SPI: begin
2
        spim_start <= 0; // release spim start
3
        o_cs <= 1'b0;
4
        if(!oled_busy) begin
5
          LUT_INDEX <= LUT_INDEX + 1'd1;
6
          machine_state <= STATE_SPI_DONE;
7
        end
8
      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.

Please log in before posting. Registration is free and takes only a minute.
Existing account
Do you have a Google/GoogleMail account? No registration required!
Log in with Google account
No account? Register here.