serial_transfer: add 'rate' command

This makes it possible to change the serial baud rate without
uploading a new serial transfer program. I'm not sure how useful this
will be, but it is simple enough to add.

The client program is also substantially improved. Sincerely I do not
understand how/why this works. Experimentally, I found that feeding
the ft232h data in chunks of up to roughly 384 bytes works reliably,
both for reads and writes. Larger chunk sizes are (as expected)
faster, but the tranfers do not appear to be consistently correct in
this case.

I have no logical explanation for this. The size of the ft232h FIFO is
1K each for the transmit and receive buffer respectively.

This also enables RTS/CTS hardware flow control. Surprisingly, this
doesn't appear to affect reliability significantly.
This commit is contained in:
Zack Buhman 2024-03-15 18:33:15 +08:00
parent 328e9e18bd
commit 1a7a8c4484
5 changed files with 121 additions and 38 deletions

View File

@ -33,10 +33,10 @@ def symmetric(ser, b):
l = []
mem = memoryview(b)
i = 0
chunk_size = 16
chunk_size = 384
while i < len(b):
if i % 128 == 0:
if i % 1024 == 0:
print(i, end=' ')
sys.stdout.flush()
while True:
@ -48,40 +48,45 @@ def symmetric(ser, b):
chunk_size = min(chunk_size, len(b) - i)
assert chunk_size > 0, chunk_size
ser.write(b[i:i+chunk_size])
ser.write(mem[i:i+chunk_size])
i += chunk_size
time.sleep(0.1)
res = ser.read(ser.in_waiting)
l.extend(res)
orig_count = 1000
count = orig_count
while count > 0:
if len(l) >= len(b):
break
time.sleep(1 / orig_count)
if ser.in_waiting:
res = ser.read(ser.in_waiting)
l.extend(res)
count -= 1
return bytes(l)
def do(ser, b):
_ = ser.read(ser.in_waiting)
ser.flush()
ser.flushInput()
ser.flushOutput()
def start_data(ser, b):
_ = ser.read(ser.in_waiting)
ret = sync(ser, b'DATA')
#print(ret)
size = len(b)
args = struct.pack("<II", size, dest)
#print("dargs", args)
ret = sync(ser, args)
#print(ret)
ret = sync(ser, b'DATA' + args)
if ret != b'data\n':
print(".", end=' ')
sys.stdout.flush()
sync(ser, b'prime', wait=0)
do(ser, b)
start_data(ser, b)
return
print("\nDATA")
def do(ser, b):
start_data(ser, b)
start = time.monotonic()
ret = symmetric(ser, b)
#print("\nHERE", len(ret), len(b))
end = time.monotonic()
duration = end - start
print("duration", duration)
print("\n\nduration:", duration, "\n\n")
print(ret[-5:])
if ret[:-5] != b:
print("ret != b; dumped to asdf.bin")
@ -90,9 +95,8 @@ def do(ser, b):
print("did not jump")
return
ret = sync(ser, b'JUMP', wait=0)
args = struct.pack("<I", dest)
ser.write(args)
ret = sync(ser, b'JUMP' + args, wait=0)
print()
console(ser)
@ -107,8 +111,45 @@ def console(ser):
with open(sys.argv[1], 'rb') as f:
b = f.read()
with serial.Serial('/dev/ttyUSB0', 120192, timeout=1) as ser:
#with serial.Serial('/dev/ttyUSB0', 312500, timeout=1) as ser:
def baudrate_from_scbrr2(n):
return 1562500 / (n+1)
def change_rate(old_rate, new_rate):
with serial.Serial(port='/dev/ttyUSB0',
baudrate=baudrate_from_scbrr2(old_rate),
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=1,
xonxoff=False,
#rtscts=False,
rtscts=True,
) as ser:
buf = b'\x00\x00\x00\x00'
start_data(ser, buf)
ret = symmetric(ser, buf)
print('ret', ret)
print("change rate", int(baudrate_from_scbrr2(new_rate)))
args = struct.pack("<I", new_rate & 0xff)
ret = sync(ser, b'RATE' + args, wait=1)
print(ret)
old_rate = 1
new_rate = 1
if old_rate != new_rate:
change_rate(old_rate, new_rate)
with serial.Serial(port='/dev/ttyUSB0',
baudrate=baudrate_from_scbrr2(new_rate),
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=1,
xonxoff=False,
#rtscts=False,
rtscts=True,
) as ser:
#console(ser)
print("waiting: ", end=' ')
sys.stdout.flush()

View File

@ -10,7 +10,7 @@ void main() __attribute__((section(".text.main")));
void main()
{
//serial::init(12);
serial::init(1);
load_init();
while (1) {

View File

@ -1,8 +1,8 @@
OUTPUT_FORMAT("elf32-shl", "elf32-shl", "elf32-shl")
MEMORY
{
p1ram : ORIGIN = 0x8c020000, LENGTH = 0xfe0000
p2ram : ORIGIN = 0xac020000, LENGTH = 0xfe0000
p1ram : ORIGIN = 0x8c010000, LENGTH = 0xff0000
p2ram : ORIGIN = 0xac010000, LENGTH = 0xff0000
ldram : ORIGIN = 0x8cfff000, LENGTH = 0x1000
}
SECTIONS

View File

@ -9,6 +9,7 @@ enum load_command {
CMD_NONE,
CMD_DATA, // DATA 0000 0000 {data}
CMD_JUMP, // JUMP 0000
CMD_RATE, // RATE 0000
};
struct load_state {
@ -48,9 +49,8 @@ void load_init()
void debug(const char * s)
{
char c;
while ((sh7091.SCIF.SCFSR2 & scif::scfsr2::tdfe::bit_mask) == 0);
while ((c = *s++)) {
using namespace scif;
while ((sh7091.SCIF.SCFSR2 & scfsr2::tdfe::bit_mask) == 0);
sh7091.SCIF.SCFTDR2 = (uint8_t)c;
}
}
@ -75,12 +75,10 @@ void jump_to_func(const uint32_t addr)
void load_recv(uint8_t c)
{
if (state.command == CMD_NONE)
state.buf[state.len++] = c;
while (1) {
switch (state.command) {
case CMD_NONE:
state.buf[state.len++] = c;
if (state.len >= 4) {
if (state.buf[0] == 'D' &&
state.buf[1] == 'A' &&
@ -103,9 +101,19 @@ void load_recv(uint8_t c)
debug("jump\n");
state.command = CMD_JUMP;
}
} else if (state.buf[0] == 'R' &&
state.buf[1] == 'A' &&
state.buf[2] == 'T' &&
state.buf[3] == 'E') {
if (state.len < 8) {
return;
} else {
debug("rate\n");
state.command = CMD_RATE;
}
} else {
move(&state.buf[0], &state.buf[4], state.len - 4);
state.len -= 4;
move(&state.buf[0], &state.buf[1], state.len - 1);
state.len -= 1;
}
} else {
return;
@ -143,6 +151,14 @@ void load_recv(uint8_t c)
debug("postjump\n");
return;
break;
case CMD_RATE:
state.len = 0;
state.command = CMD_NONE;
debug("prerate\n");
serial::init(state.addr1 & 0xff);
debug("postrate\n");
return;
break;
}
}
}

View File

@ -9,22 +9,43 @@
namespace serial {
static inline void init_wait()
{
sh7091.TMU.TSTR &= (~tmu::tstr::str1::counter_start) & 0xff; // stop TCNT1
sh7091.TMU.TOCR = tmu::tocr::tcoe::tclk_is_external_clock_or_input_capture;
sh7091.TMU.TCR1 = tmu::tcr1::tpsc::p_phi_1024; // 1024 / 50MHz = 20.48 μs
sh7091.TMU.TCOR1 = 0xffff'ffff;
sh7091.TMU.TCNT1 = 0xffff'ffff;
sh7091.TMU.TSTR |= tmu::tstr::str1::counter_start;
uint32_t start = sh7091.TMU.TCNT1;
while ((start - sh7091.TMU.TCNT1) < 1);
sh7091.TMU.TSTR &= (~tmu::tstr::str1::counter_start) & 0xff; // stop TCNT1
}
void init(uint8_t bit_rate)
{
using namespace scif;
sh7091.SCIF.SCSCR2 = 0;
sh7091.SCIF.SCSMR2 = 0;
sh7091.SCIF.SCBRR2 = bit_rate; // bps = 1562500 / (SCBRR2 + 1)
sh7091.SCIF.SCSCR2 = 0; // disable transmission / reception
sh7091.SCIF.SCSPTR2 = 0; // clear output data pins
sh7091.SCIF.SCFCR2 = scfcr2::tfrst::reset_operation_enabled
| scfcr2::rfrst::reset_operation_enabled;
sh7091.SCIF.SCFCR2 = scfcr2::rtrg::trigger_on_1_byte
| scfcr2::ttrg::trigger_on_8_bytes
| scfcr2::mce::modem_signals_disabled;
| scfcr2::mce::modem_signals_enabled;
sh7091.SCIF.SCSMR2 = scsmr2::chr::_8_bit_data
| scsmr2::pe::parity_disabled
| scsmr2::stop::_1_stop_bit
| scsmr2::cks::p_phi_clock;
sh7091.SCIF.SCBRR2 = bit_rate; // bps = 1562500 / (SCBRR2 + 1)
sh7091.SCIF.SCSPTR2 = 0;
sh7091.SCIF.SCFSR2 = (~scfsr2::er::bit_mask)
& (~scfsr2::tend::bit_mask)
& (~scfsr2::tdfe::bit_mask)
@ -32,10 +53,15 @@ void init(uint8_t bit_rate)
& (~scfsr2::rdf::bit_mask)
& (~scfsr2::dr::bit_mask)
& 0xffff;
sh7091.SCIF.SCLSR2 = 0; // clear ORER
// wait 1 bit interval
init_wait();
sh7091.SCIF.SCSCR2 = scscr2::te::transmission_enabled
| scscr2::re::reception_enabled;
sh7091.SCIF.SCLSR2 = 0; // clear ORER
}
void character(const char c)