New simulator.
[deliverable/binutils-gdb.git] / sim / m68hc11 / dv-m68hc11spi.c
1 /* dv-m68hc11spi.c -- Simulation of the 68HC11 SPI
2 Copyright (C) 2000 Free Software Foundation, Inc.
3 Written by Stephane Carrez (stcarrez@worldnet.fr)
4 (From a driver model Contributed by Cygnus Solutions.)
5
6 This file is part of the program GDB, the GNU debugger.
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21
22 */
23
24
25 #include "sim-main.h"
26 #include "hw-main.h"
27 #include "dv-sockser.h"
28 #include "sim-assert.h"
29
30
31 /* DEVICE
32
33 m68hc11spi - m68hc11 SPI interface
34
35
36 DESCRIPTION
37
38 Implements the m68hc11 Synchronous Serial Peripheral Interface
39 described in the m68hc11 user guide (Chapter 8 in pink book).
40 The SPI I/O controller is directly connected to the CPU
41 interrupt. The simulator implements:
42
43 - SPI clock emulation
44 - Data transfer
45 - Write collision detection
46
47
48 PROPERTIES
49
50 None
51
52
53 PORTS
54
55 reset (input)
56
57 Reset port. This port is only used to simulate a reset of the SPI
58 I/O controller. It should be connected to the RESET output of the cpu.
59
60 */
61
62
63
64 /* port ID's */
65
66 enum
67 {
68 RESET_PORT
69 };
70
71
72 static const struct hw_port_descriptor m68hc11spi_ports[] =
73 {
74 { "reset", RESET_PORT, 0, input_port, },
75 { NULL, },
76 };
77
78
79 /* SPI */
80 struct m68hc11spi
81 {
82 /* Information about next character to be transmited. */
83 unsigned char tx_char;
84 int tx_bit;
85 unsigned char mode;
86
87 unsigned char rx_char;
88 unsigned char rx_clear_scsr;
89 unsigned char clk_pin;
90
91 /* SPI clock rate (twice the real clock). */
92 unsigned int clock;
93
94 /* Periodic SPI event. */
95 struct hw_event* spi_event;
96 };
97
98
99
100 /* Finish off the partially created hw device. Attach our local
101 callbacks. Wire up our port names etc */
102
103 static hw_io_read_buffer_method m68hc11spi_io_read_buffer;
104 static hw_io_write_buffer_method m68hc11spi_io_write_buffer;
105 static hw_port_event_method m68hc11spi_port_event;
106 static hw_ioctl_method m68hc11spi_ioctl;
107
108 #define M6811_SPI_FIRST_REG (M6811_SPCR)
109 #define M6811_SPI_LAST_REG (M6811_SPDR)
110
111
112 static void
113 attach_m68hc11spi_regs (struct hw *me,
114 struct m68hc11spi *controller)
115 {
116 hw_attach_address (hw_parent (me), 0, io_map,
117 M6811_SPI_FIRST_REG,
118 M6811_SPI_LAST_REG - M6811_SPI_FIRST_REG + 1,
119 me);
120 }
121
122 static void
123 m68hc11spi_finish (struct hw *me)
124 {
125 struct m68hc11spi *controller;
126
127 controller = HW_ZALLOC (me, struct m68hc11spi);
128 me->overlap_mode_hw = 1;
129 set_hw_data (me, controller);
130 set_hw_io_read_buffer (me, m68hc11spi_io_read_buffer);
131 set_hw_io_write_buffer (me, m68hc11spi_io_write_buffer);
132 set_hw_ports (me, m68hc11spi_ports);
133 set_hw_port_event (me, m68hc11spi_port_event);
134 #ifdef set_hw_ioctl
135 set_hw_ioctl (me, m68hc11spi_ioctl);
136 #else
137 me->to_ioctl = m68hc11spi_ioctl;
138 #endif
139
140 /* Attach ourself to our parent bus. */
141 attach_m68hc11spi_regs (me, controller);
142
143 /* Initialize to reset state. */
144 controller->spi_event = NULL;
145 controller->rx_clear_scsr = 0;
146 }
147
148
149
150 /* An event arrives on an interrupt port */
151
152 static void
153 m68hc11spi_port_event (struct hw *me,
154 int my_port,
155 struct hw *source,
156 int source_port,
157 int level)
158 {
159 SIM_DESC sd;
160 struct m68hc11spi *controller;
161 sim_cpu* cpu;
162 unsigned8 val;
163
164 controller = hw_data (me);
165 sd = hw_system (me);
166 cpu = STATE_CPU (sd, 0);
167 switch (my_port)
168 {
169 case RESET_PORT:
170 {
171 HW_TRACE ((me, "SPI reset"));
172
173 /* Reset the state of SPI registers. */
174 controller->rx_clear_scsr = 0;
175 if (controller->spi_event)
176 {
177 hw_event_queue_deschedule (me, controller->spi_event);
178 controller->spi_event = 0;
179 }
180
181 val = 0;
182 m68hc11spi_io_write_buffer (me, &val, io_map,
183 (unsigned_word) M6811_SPCR, 1);
184 break;
185 }
186
187 default:
188 hw_abort (me, "Event on unknown port %d", my_port);
189 break;
190 }
191 }
192
193 static void
194 set_bit_port (struct hw *me, sim_cpu *cpu, int port, int mask, int value)
195 {
196 /* TODO: Post an event to inform other devices that pin 'port' changes.
197 This has only a sense if we provide some device that is logically
198 connected to these pin ports (SCLK and MOSI) and that handles
199 the SPI protocol. */
200 if (value)
201 cpu->ios[port] |= mask;
202 else
203 cpu->ios[port] &= ~mask;
204 }
205
206
207 /* When a character is sent/received by the SPI, the PD2..PD5 line
208 are driven by the following signals:
209
210 B7 B6
211 -----+---------+--------+---/-+-------
212 MOSI | | | | | |
213 MISO +---------+--------+---/-+
214 ____ ___
215 CLK _______/ \____/ \__ CPOL=0, CPHA=0
216 _______ ____ __
217 \____/ \___/ CPOL=1, CPHA=0
218 ____ ____ __
219 __/ \____/ \___/ CPOL=0, CPHA=1
220 __ ____ ___
221 \____/ \____/ \__ CPOL=1, CPHA=1
222
223 SS ___ ____
224 \__________________________//___/
225
226 MISO = PD2
227 MOSI = PD3
228 SCK = PD4
229 SS = PD5
230
231 */
232
233 #define SPI_START_BIT 0
234 #define SPI_MIDDLE_BIT 1
235
236 void
237 m68hc11spi_clock (struct hw *me, void *data)
238 {
239 SIM_DESC sd;
240 struct m68hc11spi* controller;
241 sim_cpu *cpu;
242 int check_interrupt = 0;
243
244 controller = hw_data (me);
245 sd = hw_system (me);
246 cpu = STATE_CPU (sd, 0);
247
248 /* Cleanup current event. */
249 if (controller->spi_event)
250 {
251 hw_event_queue_deschedule (me, controller->spi_event);
252 controller->spi_event = 0;
253 }
254
255 /* Change a bit of data at each two SPI event. */
256 if (controller->mode == SPI_START_BIT)
257 {
258 /* Reflect the bit value on bit 2 of port D. */
259 set_bit_port (me, cpu, M6811_PORTD, (1 << 2),
260 (controller->tx_char & (1 << controller->tx_bit)));
261 controller->tx_bit--;
262 controller->mode = SPI_MIDDLE_BIT;
263 }
264 else
265 {
266 controller->mode = SPI_START_BIT;
267 }
268
269 /* Change the SPI clock at each event on bit 4 of port D. */
270 controller->clk_pin = ~controller->clk_pin;
271 set_bit_port (me, cpu, M6811_PORTD, (1 << 4), controller->clk_pin);
272
273 /* Transmit is now complete for this byte. */
274 if (controller->mode == SPI_START_BIT && controller->tx_bit < 0)
275 {
276 controller->rx_clear_scsr = 0;
277 cpu->ios[M6811_SPSR] |= M6811_SPIF;
278 if (cpu->ios[M6811_SPCR] & M6811_SPIE)
279 check_interrupt = 1;
280 }
281 else
282 {
283 controller->spi_event = hw_event_queue_schedule (me, controller->clock,
284 m68hc11spi_clock,
285 NULL);
286 }
287
288 if (check_interrupt)
289 interrupts_update_pending (&cpu->cpu_interrupts);
290 }
291
292 /* Flags of the SPCR register. */
293 io_reg_desc spcr_desc[] = {
294 { M6811_SPIE, "SPIE ", "Serial Peripheral Interrupt Enable" },
295 { M6811_SPE, "SPE ", "Serial Peripheral System Enable" },
296 { M6811_DWOM, "DWOM ", "Port D Wire-OR mode option" },
297 { M6811_MSTR, "MSTR ", "Master Mode Select" },
298 { M6811_CPOL, "CPOL ", "Clock Polarity" },
299 { M6811_CPHA, "CPHA ", "Clock Phase" },
300 { M6811_SPR1, "SPR1 ", "SPI Clock Rate Select" },
301 { M6811_SPR0, "SPR0 ", "SPI Clock Rate Select" },
302 { 0, 0, 0 }
303 };
304
305
306 /* Flags of the SPSR register. */
307 io_reg_desc spsr_desc[] = {
308 { M6811_SPIF, "SPIF ", "SPI Transfer Complete flag" },
309 { M6811_WCOL, "WCOL ", "Write Collision" },
310 { M6811_MODF, "MODF ", "Mode Fault" },
311 { 0, 0, 0 }
312 };
313
314 static void
315 m68hc11spi_info (struct hw *me)
316 {
317 SIM_DESC sd;
318 uint16 base = 0;
319 sim_cpu *cpu;
320 struct m68hc11spi *controller;
321 uint8 val;
322
323 sd = hw_system (me);
324 cpu = STATE_CPU (sd, 0);
325 controller = hw_data (me);
326
327 sim_io_printf (sd, "M68HC11 SPI:\n");
328
329 base = cpu_get_io_base (cpu);
330
331 val = cpu->ios[M6811_SPCR];
332 print_io_byte (sd, "SPCR", spcr_desc, val, base + M6811_SPCR);
333 sim_io_printf (sd, "\n");
334
335 val = cpu->ios[M6811_SPSR];
336 print_io_byte (sd, "SPSR", spsr_desc, val, base + M6811_SPSR);
337 sim_io_printf (sd, "\n");
338
339 if (controller->spi_event)
340 {
341 signed64 t;
342
343 t = hw_event_remain_time (me, controller->spi_event);
344 sim_io_printf (sd, " SPI operation finished in %ld cycles\n",
345 (long) t);
346 }
347 }
348
349 static int
350 m68hc11spi_ioctl (struct hw *me,
351 hw_ioctl_request request,
352 va_list ap)
353 {
354 m68hc11spi_info (me);
355 return 0;
356 }
357
358 /* generic read/write */
359
360 static unsigned
361 m68hc11spi_io_read_buffer (struct hw *me,
362 void *dest,
363 int space,
364 unsigned_word base,
365 unsigned nr_bytes)
366 {
367 SIM_DESC sd;
368 struct m68hc11spi *controller;
369 sim_cpu *cpu;
370 unsigned8 val;
371
372 HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
373
374 sd = hw_system (me);
375 cpu = STATE_CPU (sd, 0);
376 controller = hw_data (me);
377
378 switch (base)
379 {
380 case M6811_SPSR:
381 controller->rx_clear_scsr = cpu->ios[M6811_SCSR]
382 & (M6811_SPIF | M6811_WCOL | M6811_MODF);
383
384 case M6811_SPCR:
385 val = cpu->ios[base];
386 break;
387
388 case M6811_SPDR:
389 if (controller->rx_clear_scsr)
390 {
391 cpu->ios[M6811_SPSR] &= ~controller->rx_clear_scsr;
392 controller->rx_clear_scsr = 0;
393 }
394 val = controller->rx_char;
395 break;
396
397 default:
398 return 0;
399 }
400 *((unsigned8*) dest) = val;
401 return 1;
402 }
403
404 static unsigned
405 m68hc11spi_io_write_buffer (struct hw *me,
406 const void *source,
407 int space,
408 unsigned_word base,
409 unsigned nr_bytes)
410 {
411 SIM_DESC sd;
412 struct m68hc11spi *controller;
413 sim_cpu *cpu;
414 unsigned8 val;
415
416 HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
417
418 sd = hw_system (me);
419 cpu = STATE_CPU (sd, 0);
420 controller = hw_data (me);
421
422 val = *((const unsigned8*) source);
423 switch (base)
424 {
425 case M6811_SPCR:
426 cpu->ios[M6811_SPCR] = val;
427
428 /* The SPI clock rate is 2, 4, 16, 32 of the internal CPU clock.
429 We have to drive the clock pin and need a 2x faster clock. */
430 switch (val & (M6811_SPR1 | M6811_SPR0))
431 {
432 case 0:
433 controller->clock = 1;
434 break;
435
436 case 1:
437 controller->clock = 2;
438 break;
439
440 case 2:
441 controller->clock = 8;
442 break;
443
444 default:
445 controller->clock = 16;
446 break;
447 }
448
449 /* Set the clock pin. */
450 if ((val & M6811_CPOL)
451 && (controller->spi_event == 0
452 || ((val & M6811_CPHA) && controller->mode == 1)))
453 controller->clk_pin = 1;
454 else
455 controller->clk_pin = 0;
456
457 set_bit_port (me, cpu, M6811_PORTD, (1 << 4), controller->clk_pin);
458 break;
459
460 /* Can't write to SPSR. */
461 case M6811_SPSR:
462 break;
463
464 case M6811_SPDR:
465 if (!(cpu->ios[M6811_SPCR] & M6811_SPE))
466 {
467 return 0;
468 }
469
470 /* If transfer is taking place, a write to SPDR
471 generates a collision. */
472 if (controller->spi_event)
473 {
474 cpu->ios[M6811_SPSR] |= M6811_WCOL;
475 break;
476 }
477
478 /* Refuse the write if there was no read of SPSR. */
479 /* ???? TBD. */
480
481 /* Prepare to send a byte. */
482 controller->tx_char = val;
483 controller->tx_bit = 7;
484 controller->mode = 0;
485
486 /* Toggle clock pin internal value when CPHA is 0 so that
487 it will really change in the middle of a bit. */
488 if (!(cpu->ios[M6811_SPCR] & M6811_CPHA))
489 controller->clk_pin = ~controller->clk_pin;
490
491 cpu->ios[M6811_SPDR] = val;
492
493 /* Activate transmission. */
494 m68hc11spi_clock (me, NULL);
495 break;
496
497 default:
498 return 0;
499 }
500 return nr_bytes;
501 }
502
503
504 const struct hw_descriptor dv_m68hc11spi_descriptor[] = {
505 { "m68hc11spi", m68hc11spi_finish, },
506 { NULL },
507 };
508
This page took 0.040947 seconds and 4 git commands to generate.