@@ -12,6 +12,7 @@
#include "chardev/char-fe.h"
#include "hw/sysbus.h"
#include "qom/object.h"
+#include "qemu/fifo8.h"
#define TYPE_RENESAS_SCI "renesas-sci"
typedef struct RSCIState RSCIState;
@@ -26,13 +27,18 @@ enum {
SCI_NR_IRQ = 4
};
+enum {
+ TXTIMER = 0,
+ RXTIMER = 1,
+ NR_TIMER = 2,
+};
+
struct RSCIState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion memory;
- QEMUTimer timer;
CharBackend chr;
qemu_irq irq[SCI_NR_IRQ];
@@ -47,8 +53,9 @@ struct RSCIState {
uint8_t read_ssr;
int64_t trtime;
- int64_t rx_next;
uint64_t input_freq;
+ Fifo8 rxfifo;
+ QEMUTimer timer[NR_TIMER];
};
#endif
@@ -69,32 +69,63 @@ REG8(SEMR, 7)
FIELD(SEMR, ACS0, 0, 1)
FIELD(SEMR, ABCS, 4, 1)
-static int can_receive(void *opaque)
+enum {
+ RXFIFO_DEPTH = 16,
+};
+
+static void set_next_event(RSCIState *sci, int evt, int64_t expire)
{
- RSCIState *sci = RSCI(opaque);
- if (sci->rx_next > qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) {
- return 0;
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+ if (expire > 0) {
+ timer_mod(&sci->timer[evt], now + expire);
} else {
- return FIELD_EX8(sci->scr, SCR, RE);
+ timer_del(&sci->timer[evt]);
}
}
-static void receive(void *opaque, const uint8_t *buf, int size)
+static int can_receive(void *opaque)
{
RSCIState *sci = RSCI(opaque);
- sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime;
- if (FIELD_EX8(sci->ssr, SSR, RDRF) || size > 1) {
+ return FIELD_EX8(sci->scr, SCR, RE) ? fifo8_num_free(&sci->rxfifo) : 0;
+}
+
+static void sci_rx_event(void *opaque)
+{
+ RSCIState *sci = RSCI(opaque);
+ uint32_t rd;
+
+ if (fifo8_is_empty(&sci->rxfifo)) {
+ /* receiver idle state */
+ set_next_event(sci, RXTIMER, 0);
+ return;
+ }
+ rd = fifo8_pop(&sci->rxfifo);
+ if (FIELD_EX8(sci->ssr, SSR, RDRF)) {
+ /* Don't receive last byte */
sci->ssr = FIELD_DP8(sci->ssr, SSR, ORER, 1);
if (FIELD_EX8(sci->scr, SCR, RIE)) {
qemu_set_irq(sci->irq[ERI], 1);
}
} else {
- sci->rdr = buf[0];
+ sci->rdr = rd;
sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 1);
if (FIELD_EX8(sci->scr, SCR, RIE)) {
qemu_irq_pulse(sci->irq[RXI]);
}
}
+ set_next_event(sci, RXTIMER, sci->trtime);
+}
+
+static void receive(void *opaque, const uint8_t *buf, int size)
+{
+ RSCIState *sci = RSCI(opaque);
+ fifo8_push_all(&sci->rxfifo, buf, size);
+
+ if (!timer_pending(&sci->timer[RXTIMER])) {
+ /* reciever idle state, start rx */
+ sci_rx_event(sci);
+ }
}
static void send_byte(RSCIState *sci)
@@ -102,22 +133,26 @@ static void send_byte(RSCIState *sci)
if (qemu_chr_fe_backend_connected(&sci->chr)) {
qemu_chr_fe_write_all(&sci->chr, &sci->tdr, 1);
}
- timer_mod(&sci->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime);
+ set_next_event(sci, TXTIMER, sci->trtime);
sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 0);
sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1);
qemu_set_irq(sci->irq[TEI], 0);
- if (FIELD_EX8(sci->scr, SCR, TIE)) {
+ if (FIELD_EX8(sci->scr, SCR, TE) && FIELD_EX8(sci->scr, SCR, TIE)) {
qemu_irq_pulse(sci->irq[TXI]);
}
}
-static void txend(void *opaque)
+static void sci_tx_event(void *opaque)
{
RSCIState *sci = RSCI(opaque);
- if (!FIELD_EX8(sci->ssr, SSR, TDRE)) {
+
+ if (FIELD_EX8(sci->ssr, SSR, TDRE) == 0) {
+ /* next tx ready */
send_byte(sci);
} else {
+ /* no next tx */
sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1);
+ set_next_event(sci, TXTIMER, 0);
if (FIELD_EX8(sci->scr, SCR, TEIE)) {
qemu_set_irq(sci->irq[TEI], 1);
}
@@ -126,15 +161,15 @@ static void txend(void *opaque)
static void update_trtime(RSCIState *sci)
{
+ int64_t baseclk = NANOSECONDS_PER_SECOND / sci->input_freq;
+ baseclk *= 64 - 32 * FIELD_EX8(sci->semr, SEMR, ABCS);
+ baseclk *= 1 << (2 * FIELD_EX8(sci->smr, SMR, CKS));
+ baseclk *= sci->brr + 1;
/* char per bits */
sci->trtime = 8 - FIELD_EX8(sci->smr, SMR, CHR);
sci->trtime += FIELD_EX8(sci->smr, SMR, PE);
sci->trtime += FIELD_EX8(sci->smr, SMR, STOP) + 1;
- /* x bit transmit time (32 * divrate * brr) / base freq */
- sci->trtime *= 32 * sci->brr;
- sci->trtime *= 1 << (2 * FIELD_EX8(sci->smr, SMR, CKS));
- sci->trtime *= NANOSECONDS_PER_SECOND;
- sci->trtime /= sci->input_freq;
+ sci->trtime *= baseclk;
}
static bool sci_is_tr_enabled(RSCIState *sci)
@@ -151,23 +186,37 @@ static void sci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
if (!sci_is_tr_enabled(sci)) {
sci->smr = val;
update_trtime(sci);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: Register 0x%"
+ HWADDR_PRIX " write protected.\n", offset);
}
break;
case A_BRR:
if (!sci_is_tr_enabled(sci)) {
sci->brr = val;
update_trtime(sci);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: Register 0x%"
+ HWADDR_PRIX " write protected.\n", offset);
}
break;
case A_SCR:
- sci->scr = val;
- if (FIELD_EX8(sci->scr, SCR, TE)) {
- sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1);
- sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1);
- if (FIELD_EX8(sci->scr, SCR, TIE)) {
- qemu_irq_pulse(sci->irq[TXI]);
+ if (FIELD_EX8(sci->scr, SCR, TE) != FIELD_EX8(val, SCR, TE)) {
+ if (FIELD_EX8(val, SCR, TE)) {
+ /* Disable -> Enable to reset TX*/
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1);
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1);
+ if (FIELD_EX8(val, SCR, TIE)) {
+ qemu_irq_pulse(sci->irq[TXI]);
+ }
+ } else {
+ /* disable TX clock */
+ set_next_event(sci, TXTIMER, 0);
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 0);
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1);
}
}
+ sci->scr = val;
if (!FIELD_EX8(sci->scr, SCR, TEIE)) {
qemu_set_irq(sci->irq[TEI], 0);
}
@@ -177,10 +226,14 @@ static void sci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
break;
case A_TDR:
sci->tdr = val;
- if (FIELD_EX8(sci->ssr, SSR, TEND)) {
- send_byte(sci);
+ if (FIELD_EX8(sci->scr, SCR, TE)) {
+ if (FIELD_EX8(sci->ssr, SSR, TEND)) {
+ send_byte(sci);
+ } else {
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 0);
+ }
} else {
- sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 0);
+ qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: Transmit disabled.\n");
}
break;
case A_SSR:
@@ -224,6 +277,9 @@ static uint64_t sci_read(void *opaque, hwaddr offset, unsigned size)
sci->read_ssr = sci->ssr;
return sci->ssr;
case A_RDR:
+ if (!FIELD_EX8(sci->scr, SCR, RE)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: Receive disabled.\n");
+ }
sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 0);
return sci->rdr;
case A_SCMR:
@@ -255,7 +311,7 @@ static void rsci_reset(DeviceState *dev)
sci->ssr = 0x84;
sci->scmr = 0x00;
sci->semr = 0x00;
- sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ update_trtime(sci);
}
static void sci_event(void *opaque, QEMUChrEvent event)
@@ -263,6 +319,7 @@ static void sci_event(void *opaque, QEMUChrEvent event)
RSCIState *sci = RSCI(opaque);
if (event == CHR_EVENT_BREAK) {
sci->ssr = FIELD_DP8(sci->ssr, SSR, FER, 1);
+ sci->rdr = 0;
if (FIELD_EX8(sci->scr, SCR, RIE)) {
qemu_set_irq(sci->irq[ERI], 1);
}
@@ -295,16 +352,19 @@ static void rsci_init(Object *obj)
for (i = 0; i < SCI_NR_IRQ; i++) {
sysbus_init_irq(d, &sci->irq[i]);
}
- timer_init_ns(&sci->timer, QEMU_CLOCK_VIRTUAL, txend, sci);
+ timer_init_ns(&sci->timer[TXTIMER],
+ QEMU_CLOCK_VIRTUAL, sci_tx_event, sci);
+ timer_init_ns(&sci->timer[RXTIMER],
+ QEMU_CLOCK_VIRTUAL, sci_rx_event, sci);
+ fifo8_create(&sci->rxfifo, RXFIFO_DEPTH);
}
static const VMStateDescription vmstate_rsci = {
.name = "renesas-sci",
- .version_id = 1,
+ .version_id = 2,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_INT64(trtime, RSCIState),
- VMSTATE_INT64(rx_next, RSCIState),
VMSTATE_UINT8(smr, RSCIState),
VMSTATE_UINT8(brr, RSCIState),
VMSTATE_UINT8(scr, RSCIState),
@@ -314,7 +374,7 @@ static const VMStateDescription vmstate_rsci = {
VMSTATE_UINT8(scmr, RSCIState),
VMSTATE_UINT8(semr, RSCIState),
VMSTATE_UINT8(read_ssr, RSCIState),
- VMSTATE_TIMER(timer, RSCIState),
+ VMSTATE_TIMER_ARRAY(timer, RSCIState, NR_TIMER),
VMSTATE_END_OF_LIST()
}
};
SCI does not have a fifo, it is necessary to send and receive at a bit rate speed. But, qemu's chardev backend does not have a buffer, so it sends received data continuously. By buffering the received data with the FIFO, continuous received data can be received. Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp> --- include/hw/char/renesas_sci.h | 11 ++- hw/char/renesas_sci.c | 124 +++++++++++++++++++++++++--------- 2 files changed, 101 insertions(+), 34 deletions(-)