Root/libubb/swuart.c

1/*
2 * swuart/swuart.c - Software-implemented UART for UBB
3 *
4 * Written 2012 by Werner Almesberger
5 * Copyright 2012 Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13
14#include <stdbool.h>
15#include <string.h>
16#include <sys/mman.h>
17
18#include <ubb/ubb.h>
19#include <ubb/regs4740.h>
20#include <ubb/swuart.h>
21
22
23#define TIMER 7
24
25
26static uint32_t tx_mask, rx_mask;
27static uint32_t ticks;
28
29
30/* ----- Hardware timer ---------------------------------------------------- */
31
32
33/*
34 * We run the hardware timer at PCLK/16 = 7 MHz. This gives us 60 cycles at
35 * 115200 bps and sets the lowest data rate to 106.8 bps.
36 */
37
38#define PCLK (112*1000*1000) /* 112 MHz */
39#define TCLK (PCLK/16) /* 7 MHz */
40
41
42static void get_timer(uint32_t cycle)
43{
44    TSCR = 1 << TIMER; /* enable clock to timer */
45    TDFR(TIMER) = cycle-1; /* cycle = one bit time */
46    TCSR(TIMER) = (2 << 3) | 1; /* count at PCLK/16 */
47}
48
49
50static void reset_timer(void)
51{
52    TECR = 1 << TIMER; /* disable timer */
53    TCNT(TIMER) = 0; /* reset timer */
54    TFCR = 0x10001 << TIMER; /* clear flags */
55    TESR = 1 << TIMER; /* enable timer */
56}
57
58
59static void set_half_cycle(uint32_t cycle)
60{
61    uint32_t t;
62
63    t = (uint16_t) TCNT(TIMER)+(cycle >> 1);
64                    /* TCNT+cycle/2 mod cycle-1 */
65    if (t >= cycle-1)
66        t -= cycle-1;
67    TDHR(TIMER) = t;
68    TFCR = 0x10000 << TIMER; /* clear half flags */
69}
70
71
72static void release_timer(void)
73{
74    TECR = 1 << TIMER; /* disable timer */
75    TFCR = 0x10001 << TIMER; /* clear flags */
76    TSSR = 1 << TIMER; /* disable clock to timer */
77}
78
79
80static inline bool timer_full(void)
81{
82    if (!(TFR & (1 << TIMER)))
83        return 0;
84    TFCR = 1 << TIMER;
85    return 1;
86}
87
88
89static inline bool timer_half(void)
90{
91    if (!(TFR & (0x10000 << TIMER)))
92        return 0;
93    TFCR = 0x10000 << TIMER;
94    return 1;
95}
96
97
98/* ----- Enable/disable interrupts ----------------------------------------- */
99
100
101static uint32_t old_icmr;
102
103
104static void disable_interrupts(void)
105{
106    old_icmr = ICMR;
107    ICMSR = 0xffffffff;
108}
109
110
111static void enable_interrupts(void)
112{
113    ICMCR = ~old_icmr;
114}
115
116
117/* ----- Transmit a block -------------------------------------------------- */
118
119
120static struct swuart_err errors;
121
122
123static int trx(uint8_t *out, int out_size, uint8_t *in, int in_size,
124    unsigned wait_bits, unsigned idle_bits)
125{
126    unsigned tx, rx = 0, idle = 0;
127    int tx_bits = 0, rx_bits = 0;
128    int in_len = 0;
129
130    reset_timer();
131    while (1) {
132full:
133        /* timer has just crossed FULL */
134
135        if (tx_bits) {
136            if (tx & 1)
137                SET(tx_mask);
138            else
139                CLR(tx_mask);
140            tx >>= 1;
141            tx_bits--;
142        } else {
143            if (out_size) {
144                CLR(tx_mask);
145                tx = *out++ | 0x100;
146                out_size--;
147                tx_bits = 9;
148            } else {
149                if (idle++ == wait_bits)
150                    return in_len;
151            }
152        }
153
154        /* wait for reception (if any) */
155
156        if (rx_bits) {
157            while (!timer_half());
158        } else {
159            while (!timer_full())
160                if (!PIN(rx_mask)) {
161                    set_half_cycle(ticks);
162                    rx_bits = 10;
163                    break;
164                }
165            if (!rx_bits)
166                continue;
167            while (!timer_half())
168                if (timer_full())
169                    goto full;
170        }
171
172        /* timer has just crossed HALF */
173
174        rx = (rx >> 1) | (PIN(rx_mask) ? 0x200 : 0);
175        if (!--rx_bits) {
176            if (rx & 1) {
177                errors.glitch++;
178            } else if (!(rx & 0x200)) {
179                errors.framing++;
180            } else if (in_len == in_size) {
181                errors.overflow++;
182            } else {
183                *in++ = rx >> 1;
184                in_len++;
185                idle = 0;
186                wait_bits = idle_bits;
187            }
188        }
189
190        while (!timer_full());
191    }
192}
193
194
195int swuart_trx(void *out, int out_size, void *in, int in_size,
196    int wait_bits, int idle_bits)
197{
198    int got;
199
200    disable_interrupts();
201    get_timer(ticks);
202
203    got = trx(out, out_size, in, in_size, wait_bits, idle_bits);
204
205    release_timer();
206    enable_interrupts();
207
208    return got;
209}
210
211
212/* ----- Access error counters --------------------------------------------- */
213
214
215unsigned swuart_get_errors(struct swuart_err *res)
216{
217    unsigned sum = errors.glitch+errors.framing+errors.overflow;
218
219    if (res)
220        *res = errors;
221    return sum;
222}
223
224
225void swuart_clear_errors(void)
226{
227    memset(&errors, 0, sizeof(errors));
228}
229
230
231/* ----- Open/close the software UART -------------------------------------- */
232
233
234int swuart_open(uint32_t tx, uint32_t rx, int bps)
235{
236    /*
237     * Make sure code and rx/tx buffers are locked into memory before we
238     * disable interrupts in swuart_trx.
239     */
240    if (mlockall(MCL_CURRENT | MCL_FUTURE))
241        return -1;
242
243    ticks = TCLK/bps-1;
244    tx_mask = tx;
245    rx_mask = rx;
246
247    OUT(tx);
248    SET(tx);
249    IN(rx);
250
251    return 0;
252}
253
254
255void swuart_close(void)
256{
257    munlockall();
258}
259

Archive Download this file

Branches:
master



interactive