OpenCores
URL https://opencores.org/ocsvn/openarty/openarty/trunk

Subversion Repositories openarty

[/] [openarty/] [trunk/] [sim/] [verilated/] [oledsim.cpp] - Blame information for rev 58

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 58 dgisselq
////////////////////////////////////////////////////////////////////////////////
2
//
3
// Filename:    oledsim.cpp
4
//
5
// Project:     OpenArty, an entirely open SoC based upon the Arty platform
6
//
7
// Purpose:     The goal of this module is very specifically to simulate the 
8
//              PModOLEDrgb using a GTKMM controlled window.  I'm doing this on
9
//      an Linux computer with X-Windows, although one GTKMM selling point is
10
//      that it should work in Windows as well.  I won't vouch for that, as I
11
//      haven't tested under windows.
12
//
13
//      Either way, this controller only implements *some* of the OLED commands.
14
//      There were just too many commands for me to be able to write them in the
15
//      short order that I needed to get a test up and running.  Therefore, this
16
//      simulator will validate all commands and assure you they are valid
17
//      commands, but it will only respond to some.  For specifics, see the
18
//      do_command() section below.
19
//
20
//      You may notice a lot of assert() calls within this code.  This is half
21
//      the purpose of the code: to verify that interactions, when the take
22
//      place, are valid.  The sad problem and effect of this is simply that
23
//      when bugs are present, the error/warning messages are not that complete.
24
//      If you find yourself dealing with such an error, please feel free to
25
//      explain the assert better before asserting, and then send your 
26
//      contributions back to me so that others can benefit from your work.
27
//      (Don't you love the GPL?)
28
//
29
// Creator:     Dan Gisselquist, Ph.D.
30
//              Gisselquist Technology, LLC
31
//
32
////////////////////////////////////////////////////////////////////////////////
33
//
34
// Copyright (C) 2015-2016, Gisselquist Technology, LLC
35
//
36
// This program is free software (firmware): you can redistribute it and/or
37
// modify it under the terms of  the GNU General Public License as published
38
// by the Free Software Foundation, either version 3 of the License, or (at
39
// your option) any later version.
40
//
41
// This program is distributed in the hope that it will be useful, but WITHOUT
42
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
43
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
44
// for more details.
45
//
46
// You should have received a copy of the GNU General Public License along
47
// with this program.  (It's in the $(ROOT)/doc directory, run make with no
48
// target there if the PDF file isn't present.)  If not, see
49
// <http://www.gnu.org/licenses/> for a copy.
50
//
51
// License:     GPL, v3, as defined and found on www.gnu.org,
52
//              http://www.gnu.org/licenses/gpl.html
53
//
54
//
55
////////////////////////////////////////////////////////////////////////////////
56
//
57
//
58
#include "oledsim.h"
59
 
60
const   int     OLEDSIM::OLED_HEIGHT = 64, OLEDSIM::OLED_WIDTH = 96;
61
 
62
const   int     MICROSECOND = 81,
63
                tMINRESET = 3  * MICROSECOND, // 3 uS
64
                tCYCLE = 13, // 150 * NANOSECOND, clock cycle time 
65
                tAS    =  4, // 40 * NANOSECOND, address setup time
66
                tAH    =  4, // 40 * NANOSECOND, address hold time
67
                tCSS   =  7, // 75 * NANOSECOND, chip select setup
68
                tCSH   =  5, // 60 * NANOSECOND, chip select hold
69
                tCLKL  =  7, // 75 * NANOSECOND, time the clock must be low
70
                tCLKH  =  7; // 75 * NANOSECOND, time the clock must be high
71
 
72
void    OLEDSIM::on_realize() {
73
        Gtk::DrawingArea::on_realize();
74
 
75
        // We'll be doing all of our drawing on an off-screen bit map.  Here,
76
        // let's allocate that pixel map ...
77
        m_pix = Cairo::ImageSurface::create(Cairo::FORMAT_RGB24,
78
                        OLED_WIDTH, OLED_HEIGHT);
79
 
80
        // and a graphics context to be used when drawing to it.
81
        m_gc = Cairo::Context::create(m_pix);
82
 
83
        // We'll start the pixel map filled with all black, as this is what
84
        // my device looks like when I'm not doing anything with it.
85
        m_gc->set_source_rgb(0.0,0.0,0.0); // Black
86
        m_gc->rectangle(0, 0, OLED_WIDTH, OLED_HEIGHT);
87
        m_gc->fill();
88
}
89
 
90
void    OLEDSIM::get_preferred_width_vfunc(int &min, int &nw) const {
91
        // GTKMM wants to know how big we want our window to be.
92
        // Let's request a window twice as big as we need, but insist that
93
        // it never be smaller than one pixel output per one pixel input.
94
        //
95
        min = OLED_WIDTH;
96
        nw = OLED_WIDTH * 2;
97
}
98
 
99
void    OLEDSIM::get_preferred_height_vfunc(int &min, int &nw) const {
100
        //
101
        // Same thing as above, but this time for height, not width.
102
        //
103
        min = OLED_HEIGHT;
104
        nw = OLED_HEIGHT * 2;
105
}
106
 
107
void    OLEDSIM::get_preferred_width_for_height_vfunc(int h, int &min, int &nw) const {
108
        min = OLED_WIDTH;
109
        int k = (h+(OLED_HEIGHT/2))/OLED_HEIGHT;
110
        if (k <= 0)
111
                k = 1;
112
        nw = OLED_WIDTH * k;
113
}
114
 
115
void    OLEDSIM::get_preferred_height_for_width_vfunc(int w, int &min, int &nw) const {
116
        min = OLED_HEIGHT;
117
        int k = (w+(OLED_WIDTH/2))/OLED_WIDTH;
118
        if (k <= 0)
119
                k = 1;
120
        nw = OLED_HEIGHT * k;
121
}
122
 
123
/*
124
 * This is our simulation function.  This is the function that gets called at
125
 * every tick of our controller within Verilator.  At each tick (and not twice
126
 * per tick), the outputs are gathered and sent our way.  Here, we just decode
127
 * the power and reset outputs, and send everything else to handle_io().
128
 */
129
void    OLEDSIM::operator()(const int iopwr, const int rstn, const int dpwr,
130
                const int csn, const int sck, const int dcn, const int mosi) {
131
        if (!iopwr) {
132
                if (m_state != OLED_OFF) {
133
                        m_state = OLED_OFF;
134
                        clear_to(0.0);
135
                        queue_draw_area(0,0,get_width(), get_height());
136
                }
137
                assert(!dpwr);
138
        } else if (!rstn) {
139
                if (m_state != OLED_RESET) {
140
                        m_state = OLED_RESET;
141
                        m_locked = true;
142
                        clear_to(0.1);
143
                        m_reset_clocks = 0;
144
                        queue_draw_area(0,0,get_width(), get_height());
145
                } if (m_reset_clocks < tMINRESET)
146
                        m_reset_clocks++;
147
                assert(csn);
148
                assert(sck);
149
        } else if (dpwr) {
150
                if (m_state != OLED_POWERED) {
151
                        m_state = OLED_POWERED;
152
                        queue_draw_area(0,0,get_width(), get_height());
153
                        if (!csn) {
154
                                printf("OLED-ERR: CSN=%d, SCK=%d, DCN=%d, MOSI=%d, from %d,%d,%d\n",
155
                                csn, sck, dcn, mosi,
156
                                m_last_csn, m_last_sck, m_last_dcn);
157
                        }
158
                        assert(csn); // Can't power up with SPI active.
159
                }
160
 
161
                handle_io(csn, sck, dcn, mosi);
162
        } else {
163
                if (m_state != OLED_VIO) {
164
                        m_state = OLED_VIO;
165
                        queue_draw_area(0,0,OLED_WIDTH, OLED_HEIGHT);
166
                }
167
                handle_io(csn, sck, dcn, mosi);
168
        }
169
}
170
 
171
/* handle_io()
172
 *
173
 * We only enter this function if the I/O is powered up and the device is out
174
 * of reset.  The device may (or may not) be on.  Our purpose here is to decode
175
 * the SPI commands into a byte sequence, kept in m_data with a length given by
176
 * m_idx.  Once a command has completed, we call do_command() to actually
177
 * process the values received, the arguments, etc. and do something with them.
178
 *
179
 */
180
void    OLEDSIM::handle_io(const int csn, const int sck, const int dcn, const int mosi) {
181
        if ((csn != m_last_csn)||(sck != m_last_sck)||(dcn != m_last_dcn))
182
                printf("OLED: HANDLE-IO(%d,%d,%d,%d) @[%d]%d\n",
183
                        csn, sck, dcn, mosi, m_idx, m_bitpos);
184
        if (csn) {
185
                // CSN is high when the chip isn't selected.
186
                if (!m_last_csn) {
187
                        // If the chip was just selected, it then means that our
188
                        // command just completed.  Let's process it here.
189
                        printf("OLED: Ending a command\n");
190
                        assert(m_idx > 0);
191
                        assert((m_bitpos&7)==0);
192
                        do_command(m_last_dcn, m_idx, m_data);
193
 
194
                        m_bitpos = 0;
195
                        m_idx    = 0;
196
                        for(int i=0; i<8; i++)
197
                                m_data[i] = 0;
198
                        assert(m_last_sck);
199
                } if (!sck)
200
                        printf("OLED: CSN = %d, SCK = %d, DCN = %d, MOSI = %d, from %d, %d, %d\n",
201
                                csn, sck, dcn, mosi,
202
                                m_last_csn, m_last_sck, m_last_dcn);
203
                assert(sck);
204
                m_bitpos = 0;
205
                m_idx    = 0;
206
        } else {
207
                if (m_last_csn) {
208
                        assert((sck)&&(m_last_sck));
209
                        assert(m_last_sck);
210
                        printf("OLED: Starting a command\n");
211
                }
212
 
213
                /*
214
                if (m_last_dcn != dcn) {
215
                        m_address_counts = 0;
216
                } m_address_counts++;
217
                */
218
 
219
                if ((sck)&&(!m_last_sck)) {
220
                        m_bitpos++;
221
                        m_data[m_idx] = (m_data[m_idx]<<1)|mosi;
222
                        printf("OLED: Accepted bit: m_data[%d] = %02x\n",
223
                                m_idx, m_data[m_idx]);
224
                        if (m_bitpos >= 8) {
225
                                m_idx++;
226
                                m_bitpos &= 7;
227
                        }
228
                        assert(m_idx < 3+4+4);
229
                        // assert(m_address_count > tCSS);
230
                } else if ((!sck)&&(m_last_sck)) {
231
                }
232
        }
233
 
234
        m_last_csn = csn;
235
        m_last_sck = sck;
236
        m_last_dcn = dcn;
237
}
238
 
239
void    OLEDSIM::do_command(const int dcn, const int len, char *data) {
240
        assert(len > 0);
241
        assert(len <= 11);
242
 
243
        printf("OLED: RECEIVED CMD(%02x) ", data[0]&0x0ff);
244
        if (len > 1) {
245
                printf(" - ");
246
                for(int i=1; i<len-1; i++)
247
                        printf("%02x:", data[i]&0x0ff);
248
                printf("%02x", data[len-1]&0x0ff);
249
                printf("\n");
250
        }
251
 
252
        if (dcn) {
253
                // Do something with the pixmap
254
                double  dr, dg, db;
255
 
256
                if (m_format == OLED_65kCLR) {
257
                        int     r, g, b;
258
                        assert(len == 2);
259
                        r =  (data[0]>>3)&0x01f;
260
                        g = ((data[0]<<3)&0x038)|((data[1]>>5)&0x07);
261
                        b = ((data[1]   )&0x01f);
262
 
263
                        dr = r / 31.0;
264
                        dg = g / 63.0;
265
                        db = b / 31.0;
266
                } else {
267
                        printf("OLED: UNSUPPORTED COLOR FORMAT!\n");
268
                        dr = dg = db = 0.0;
269
                } set_gddram(m_col, m_row, dr, dg, db);
270
                if (!m_vaddr_inc) {
271
                        m_col++;
272
                        if (m_col > m_col_end) {
273
                                m_col = m_col_start;
274
                                m_row++;
275
                                if (m_row > m_row_end)
276
                                        m_row = m_row_start;
277
                        }
278
                } else {
279
                        m_row++;
280
                        if (m_row > m_row_end) {
281
                                m_row = m_row_start;
282
                                m_col++;
283
                                if (m_col > m_col_end)
284
                                        m_col = m_col_start;
285
                        }
286
                }
287
        } else if (m_locked) {
288
                if ((len == 2)&&((data[0]&0x0ff) == 0x0fd)&&(data[1] == 0x12)) {
289
                        m_locked = false;
290
                        printf("OLED: COMMANDS UNLOCKED\n");
291
                } else {
292
                        printf("OLED: COMMAND IGNORED, IC LOCKED\n");
293
                }
294
        } else {
295
                // Command word
296
                switch((data[0])&0x0ff) {
297
                case 0x15: // Setup column start and end address
298
                        assert(len == 3);
299
                        assert((data[1]&0x0ff) <= 95);
300
                        assert((data[2]&0x0ff) <= 95);
301
                        m_col_start = data[1]&0x0ff;
302
                        m_col_end   = data[2]&0x0ff;
303
                        assert(m_col_end >= m_col_start);
304
                        m_col = m_col_start;
305
                        break;
306
                case 0x75: // Setup row start and end address
307
                        assert(len == 3);
308
                        assert((data[1]&0x0ff) <= 63);
309
                        assert((data[2]&0x0ff) <= 63);
310
                        assert(m_row_end >= m_row_start);
311
                        m_row_start = data[1]&0x0ff;
312
                        m_row_end   = data[2]&0x0ff;
313
                        break;
314
                case 0x81: // Set constrast for all color "A" segment
315
                        assert(len == 2);
316
                        break;
317
                case 0x82: // Set constrast for all color "B" segment
318
                        assert(len == 2);
319
                        break;
320
                case 0x83: // Set constrast for all color "C" segment
321
                        assert(len == 2);
322
                        break;
323
                case 0x87: // Set master current attenuation factor
324
                        assert(len == 2);
325
                        break;
326
                case 0x8a: // Set second pre-charge speed, color A
327
                        assert(len == 2);
328
                        break;
329
                case 0x8b: // Set second pre-charge speed, color B
330
                        assert(len == 2);
331
                        break;
332
                case 0x8c: // Set second pre-charge speed, color C
333
                        assert(len == 2);
334
                        break;
335
                case 0xa0: // Set driver remap and color depth
336
                        assert(len == 2);
337
                        m_vaddr_inc = (data[1]&1)?true:false;
338
                        // m_fliplr = (data[1]&2)?true:false;
339
                        if ((data[1] & 0x0c0)==0)
340
                                m_format = OLED_256CLR;
341
                        else if ((data[1] & 0x0c0)==0x40)
342
                                m_format = OLED_65kCLR;
343
                        // else if ((data[1] & 0x0c0)==0x80)
344
                                // m_format = OLED_65kCLRTWO;
345
 
346
                        break;
347
                case 0xa1: // Set display start line register by row
348
                        assert(len == 2);
349
                        break;
350
                case 0xa2: // Set vertical offset by com
351
                        assert(len == 2);
352
                        break;
353
                case 0xa4: // Set display mode
354
                case 0xa5: // Fallthrough
355
                case 0xa6: // Fallthrough
356
                case 0xa7: // Fallthrough
357
                        assert(len == 1);
358
                        break;
359
                case 0xa8: // Set multiplex ratio
360
                        assert(len == 2);
361
                        break;
362
                case 0xab: // Dim Mode setting
363
                        assert(len == 6);
364
                        break;
365
                case 0xad:
366
                        assert(len == 2);
367
                        assert((data[1]&0x0fe)==0x08e);
368
                        break;
369
                case 0xac:
370
                case 0xae:
371
                case 0xaf:
372
                        assert(len == 1);
373
                        break;
374
                case 0xb0: // Power save mode
375
                        assert((len == 2)&&((data[1] == 0x1a)||(data[1] == 0x0b)));
376
                        break;
377
                case 0xb1: // Phase 1 and 2 period adjustment
378
                        assert(len == 2);
379
                        break;
380
                case 0xb3: // Displaky clock divider/oscillator frequency
381
                        assert(len == 2);
382
                        break;
383
                case 0xb8: // Set gray scale table
384
                        assert(0 && "Gray scale table not implemented");
385
                        break;
386
                case 0xb9: // Enable Linear Gray Scale table
387
                        assert(len == 1);
388
                        break;
389
                case 0xbb: // Set pre-charge level
390
                        assert(len == 2);
391
                        break;
392
                case 0xbc: // NOP
393
                case 0xbd: // NOP
394
                        assert(len == 1);
395
                case 0xbe: // Set V_COMH
396
                        assert(len == 2);
397
                        break;
398
                case 0xe3: // NOP
399
                        assert(len == 1);
400
                        break;
401
                case 0xfd: // Set command lock
402
                        assert(len == 2);
403
                        if (data[1] == 0x16) {
404
                                m_locked = true;
405
                                printf("OLED: COMMANDS NOW LOCKED\n");
406
                        }
407
                        break;
408
                case 0x21: // Draw Line
409
                        assert(len == 8);
410
                        break;
411
                case 0x22: // Draw Rectangle
412
                        assert(len == 11);
413
                        break;
414
                case 0x23: // Copy
415
                        assert(len == 7);
416
                        break;
417
                case 0x24: // Dim Window
418
                        assert(len == 5);
419
                        break;
420
                case 0x25: // Clear Window
421
                        assert(len == 5);
422
                        break;
423
                case 0x26: // Fill Enable/Disable
424
                        assert(len == 2);
425
                        // if (data[0]&1)
426
                        //      m_drect_fills = 1;
427
                        assert((data[1] & 0x10)==0);
428
                        break;
429
                case 0x27: // Continuous horizontal and vertical scrolling setup
430
                        assert(len == 6);
431
                        break;
432
                case 0x2e: // Deactivate scrolling
433
                        assert(len == 1);
434
                        // m_scrolling = false;
435
                        break;
436
                case 0x2f: // Activate scrolling
437
                        assert(len == 1);
438
                        // m_scrolling = true;
439
                        break;
440
                default:
441
                        printf("OLED: UNKNOWN COMMAND, data[0] = %02x\n", data[0] & 0x0ff);
442
                        assert(0);
443
                        break;
444
                }
445
        }
446
}
447
 
448
/*
449
 * set_gddram()
450
 *
451
 * Set graphics display DRAM.
452
 *
453
 * Here is the heart of drawing on the device, or at least pixel level drawing.
454
 * The device allows other types of drawing, such as filling rectangles and
455
 * such.  Here, we just handle the setting of pixels.
456
 *
457
 * You'll note that updates to the drawing area are only queued if the device
458
 * is in powered mode.
459
 *
460
 * At some point, I may wish to implement scrolling.  If/when that happens,
461
 * the GDDRAM will not be affected, but the area that needs to be redrawn will
462
 * be.  Hence this routine will need to be adjusted at that time.
463
 */
464
void    OLEDSIM::set_gddram(const int col, const int row,
465
                const double dr, const double dg, const double db) {
466
        // Set our color to that given by the rgb (double) parameters.
467
        m_gc->set_source_rgb(dr, dg, db);
468
 
469
        printf("OLED: Setting pixel[%2d,%2d]\n", col, row);
470
        int     drow; //  dcol;
471
        drow = row + m_display_start_row;
472
        if (drow >= OLED_HEIGHT)
473
                drow -= OLED_HEIGHT;
474
        m_gc->rectangle(col, row, 1, 1);
475
        m_gc->fill();
476
 
477
        if (m_state == OLED_POWERED) {
478
                // Need to adjust the invalidated area if scrolling is taking
479
                // place.
480
                double  kw, kh;
481
                kw = get_width()/(double)OLED_WIDTH;
482
                kh = get_height()/(double)OLED_HEIGHT;
483
                queue_draw_area(col*kw, row*kh, (int)(kw+0.5), (int)(kh+0.5));
484
        }
485
}
486
 
487
/*
488
 * clear_to()
489
 *
490
 * Clears the simulated device to a known grayscale value.  Examples are
491
 * 0.0 for black, or 0.1 for a gray that is nearly black.  Note that this
492
 * call does *not* invalidate our window.  Perhaps it should, but for now that
493
 * is the responsibility of whatever function calls this function.
494
 */
495
void    OLEDSIM::clear_to(double v) {
496
        // How do we apply this to our pixmap?
497
        m_gc->set_source_rgb(v, v, v);
498
        m_gc->rectangle(0, 0, OLED_WIDTH, OLED_HEIGHT);
499
        m_gc->fill();
500
}
501
 
502
bool    OLEDSIM::on_draw(CONTEXT &gc) {
503
        gc->save();
504
        if (m_state == OLED_POWERED) {
505
                // Scrolling will be implemented here
506
                gc->set_source(m_pix, 0, 0);
507
                gc->scale(get_width()/(double)OLED_WIDTH,
508
                                get_height()/(double)OLED_HEIGHT);
509
                gc->paint();
510
        } else {
511
                if ((m_state == OLED_VIO)||(m_state == OLED_RESET))
512
                        gc->set_source_rgb(0.1,0.1,0.1); // DARK gray
513
                else
514
                        gc->set_source_rgb(0.0,0.0,0.0); // Black
515
                // gc->rectangle(0, 0, OLED_WIDTH, OLED_HEIGHT);
516
                gc->rectangle(0, 0, get_width(), get_height());
517
                gc->fill();
518
        } gc->restore();
519
 
520
        return true;
521
}
522
 
523
OLEDWIN::OLEDWIN(void) {
524
        m_sim = new OLEDSIM();
525
        m_sim->set_size_request(OLEDSIM::OLED_WIDTH, OLEDSIM::OLED_HEIGHT);
526
        set_border_width(0);
527
        add(*m_sim);
528
        show_all();
529
        Gtk::Window::set_title(Glib::ustring("OLED Simulator"));
530
}
531
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.