Serial ports hi-speed (>115200)



  • I'm testing an Omega2 as a simple way to access a serial port via ssh. Just a simple script to redirect stdin/out via ttyS1. It works well- ssh in, run script, terminal is now interactive with serial port, press ctrl-q to exit script. I also have an esp32 programmed to basically do the same thing via simple telnet access. They each have advantages/disadvantages, but I prefer the Omega2.

    My question is how to get baud rate greater than 115200 (230400 does not work). I'm guessing a 'standard' serial driver is used and the available hispeed bits are not used (7688 datasheet shows UARTn+0024h HIGH SPEED UART which has a couple SPEED bits that look like could be used to get higher baud rates by reducing sample rate).

    I am unable to locate where the serial driver source code originates from, as I would like to see what it is doing (or not doing) to verify my assumption that the hispeed bits are unused. Is there somewhere I can get or view the serial port driver?

    For now, I'm trying to find a simple way to set those hispeed bits to test (stty to set baud, then set a bit to double effective baud rate). Do I need to create a little utility to access this register via /dev/mem and mmap? Busybox does not have the devmem option, so cannot use that. Any other quick/simple method to set a bit or two in a hardware register?

    Any suggestions welcomed.
    Thanks



  • @C-V007
    Well, I am using Go(lang) for now since it can cross-compile for mips. Crash course in Go (I have used it before briefly, but it certainly takes some getting used to again)- I can write to the registers using mmap on /dev/mem, and the results seem to indicate the uart does not like the reduced sample rate. The hispeed=1 does work, and I can get 230400 when I also set the divlatch value myself (using the 115200 value set by stty is off by one and causes it not to work, so I cannot simply just set hispeed to 1).

    So I guess it looks like I can get 230400 if I set hispeed to 1 and also set the divlatch value.(DLM:DLL). A little c utility could do it but now not sure it is worth the trouble. Maybe there is a good reason they don't mess with the hispeed register (doesn't work well).



  • Can anyone point me to where the serial driver source code may be? Just would like to take a look.

    In case anyone wants a quick way to view/set registers, here is my Go program I created. There are better ways I'm sure, but this was a quick way to get the job done without having a C/C++ cross compiler. You end up with a relatively large program (1.4MB) but it has no dependencies. It seems to work ok, but you can imagine things can go wrong if writing directly to registers :)

    package main
    /*
    devman.go
    devmem for Omega2 - read/write hardware registers via /dev/mem
    
    Go 1.11.2 32bit downloaded/extracted into $HOME/Programming/go/1.11.2
    (not installed, so use export below when at bash prompt)
    
    $export GOROOT=$HOME/Programming/go/1.11.2
    $export GOPATH=$HOME/Programming/go/work
    $export PATH=$PATH:$GOROOT/bin:$GOPATH/bin/linux_386
    $export GOARCH=386
    $export GOBIN=$GOPATH/bin/linux_386
    
    bash shell at src folder, cross compile for Omega2-
    
    $GOOS=linux GOARCH=mipsle go build -ldflags="-s -w" devmem.go
    
    then copy to Omega2-
    $scp devmem root@Omega_SNAP1:/root
    */
    
    import (
        "fmt"
        "os"
        "syscall"
        "unsafe"
        "strconv"
        "path"
    )
    
    const REGBASE = 0x10000000
    
    func usage() {
        pnam := path.Base(os.Args[0])
        fmt.Printf("\n%s read <addr>\n", pnam)
        fmt.Printf("%s write <addr> <val>\n", pnam)
        fmt.Printf("%s setbits <addr> <bitmask>\n", pnam)
        fmt.Printf("%s clrbits <addr> <bitmask>\n", pnam)
        fmt.Printf("  <addr>    = absolute or offset from 0x10000000\n")
        fmt.Printf("  <bitmask> = bits to set or clear\n\n")
        return
    }
    
    func memread(mem []byte, addr int, addr_offset int) {
        v := *(*uint32)(unsafe.Pointer(&mem[addr_offset]))
        v3 := uint8(v>>24); v2 := uint8(v>>16); v1 := uint8(v>>8); v0 := uint8(v)
        fmt.Printf(
            "\n" +
            "addr: [0x%08X] val: [0x%08X]\n" +
            "\n" +
            "bit 3 3 2 2 2 2 2 2  2 2 2 1 1 1 1 1  1 1 1 1 1 1\n" +
            "pos 1 0 9 8 7 6 5 4  3 2 1 0 9 8 7 6  5 4 3 2 1 0 9 8  7 6 5 4 3 2 1 0\n" +
            "    ...............  ...............  ...............  ...............\n",
            addr, v)
        fmt.Printf(
            "bin ")
        vb := v
        for i := 0; i < 32; i++ {
            b := "0 "
            if (vb & (1<<31)) != 0 { b = "1 " }
            fmt.Printf("%s", b)
            if (i & 7) == 7 { fmt.Printf(" ") }
            vb <<= 1
        }
        fmt.Printf(
            "\n" +
            "hex %11s0x%02X  %11s0x%02X  %11s0x%02X  %11s0x%02X\n" +
            "dec %15d  %15d  %15d  %15d\n\n",
            " ", v3, " ", v2, " ", v1, " ", v0, v3, v2, v1, v0)
    }
    func memwrite(mem []byte, addr_offset int, val uint32) {
        *(*uint32)(unsafe.Pointer(&mem[addr_offset])) = uint32(val)
    }
    func memsetbits(mem []byte, addr_offset int, val uint32) {
        *(*uint32)(unsafe.Pointer(&mem[addr_offset])) |= uint32(val)
    }
    func memclrbits(mem []byte, addr_offset int, val uint32) {
        *(*uint32)(unsafe.Pointer(&mem[addr_offset])) &^= uint32(val)
    }
    
    func main() {
        args := os.Args[1:]
        if len(args) < 2 { usage(); return }
        opt := args[0]
        a, err := strconv.ParseUint(args[1], 0, 32)
        if err != nil { fmt.Printf("invalid address- %s\n", args[1]); return }
        b := uint64(0)
        switch opt {
            case "read":
            case "write":   fallthrough
            case "setbits": fallthrough
            case "clrbits":
                if len(args) < 3 { usage(); return }
                b, err = strconv.ParseUint(args[2], 0, 32)
                if err != nil { fmt.Printf("invalid number- %s\n", args[2]); return }
            default: usage(); return
        }
    
        addr := int(a) &^ 3 | REGBASE   //word aligned, to full address
        val := uint32(b)
        page_addr := addr &^ 0xFFF      //align on page for mmap
        addr_offset := addr - page_addr //offset into mmap page
    
        f, err := os.OpenFile("/dev/mem", os.O_RDWR|os.O_SYNC, 0666)
        if err != nil { fmt.Printf("failed to open /dev/mem\n"); return }
        defer f.Close()
    
        mem, err := syscall.Mmap(int(f.Fd()), int64(page_addr), 4096,
            syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
        if err != nil { fmt.Printf("failed to mmap /dev/mem\n"); return }
    
        switch opt {
            case "read":    memread(mem, addr, addr_offset)
            case "write":   memwrite(mem, addr_offset, val)
            case "setbits": memsetbits(mem, addr_offset, val)
            case "clrbits": memclrbits(mem, addr_offset, val)
        }
    
        err = syscall.Munmap(mem)
        if err != nil { fmt.Printf("Munmap failed\n") }
    }
    


  • @C-V007 Unfortunately I can't follow / don't fully understand your posts :-( .

    You might be interested to know this...
    MediaTek MT7688 Datasheet

    5.13 UART Lite
    5.13.1 Features
    − 2-pin UART
    − 16550-compatible register set, except for Divisor Latch register
    − 5-8 data bits
    − 1-2 stop bits (1 or 2 stop bits are supported with 5 data bits)
    − Even, odd, stick or no parity
    All standard baud rates up to 345600 b/s
    − 16-byte receive buffer
    − 16-byte transmit buffer
    − Receive buffer threshold interrupt
    − Transmit buffer threshold interrupt
    − False start bit detection in asynchronous mode
    − Internal diagnostic capabilities
    − Break simulation
    − Loop-back control for communications link fault isolation

    So I simply tried to transmit with a little echo script and receive with cat using 230400, 345600 and 460800 b/s.
    Omega2+ FW 0.2.2 b200 >> Ubuntu 18.04.1 LTS desktop PC with an FTDI232 module (and vice versa of course).

    • 230340 bps / Baud
      stty -F /dev/ttyS1 230400 and stty -F /dev/ttyUSB0 230400
      these settings were OK
    • 345600 bps / Baud
      stty -F /dev/ttyS1 345600 and stty -F /dev/ttyUSB0 345600
      stty: invalid argument '345600'
      ie. I wasn't able to set 345600 b/s
      [Edit]
      I was able to set it with
      screen /dev/ttyS1 345600 and screen /dev/ttyUSB0 345600
      these settings were OK
    • 460800 bps / Baud
      stty -F /dev/ttyS1 460800 and stty -F /dev/ttyUSB0 460800
      these settings were not OK - as expected


  • @György-Farkas said in Serial ports hi-speed (>115200):

    Unfortunately I can't follow / don't fully understand your posts :-(

    No surprise, I can ramble on without making a lot of sense.

    It turns out I missed the SAMPLE_COUNT and SAMPLE_POINT registers used when HIGHSPEED = 3 (which is what you want to use for higher baud rates).

    I can now get 460800 but not 921600 (I don't know where inside that range it will stop working). I simply write to hardware registers (via my utility) after starting the ttyS1 script. A little crude, but it shows the uarts can handle high(er) speeds.

    edit- it looks like 600000 baud is ok, 650000 is not, so somewhere in that range it quits working. When writing to the registers yourself you can of course choose any baud you want just as you can with any microcontroller on the other end.

    #460800baud = 40000000/460800 = 86.8
    ./devmem write 0xd24 3          #UART1_HIGHSPEED = 3
    ./devmem setbits 0xd0c 0x80     #UART1_LCR.DLAB = 1
    ./devmem write 0xd00 1          #UART1_DLL (DLM:DLL = 1)
    ./devmem write 0xd04 0          #UART1_DLM
    ./devmem clrbits 0xd0c 0x80     #UART1_LCR.DLAB = 0
    ./devmem write 0xd28 87         #UART1_SAMPLE_COUNT = 87
    ./devmem write 0xd2c 43         #UART1_SAMPLE_POINT = 43
    

    I have the cross-compiler built and have been digging around in that folder and cannot seem to find where the serial drivers are. Looking at the files/folders in that cross-compile folder leads me to believe that I'm not asking a simple question. I think a standard 8250 serial driver is being used, but that is as far as I can get.

    I'm sure it would be better to have the serial driver do the work, but maybe its much simpler to just do my own register setting when needing higher speeds.



  • I'll quit posting as I may be the only one wanting >115200 baud :)

    summary-
    If you dig around long enough you will find the driver used is in the kernel which makes sense as the kernel will will want to output info while booting. The driver used is the 'standard' 8250 serial driver found in the linux kernel drivers folder. Through a combination of the kernel config, the drivers files, the Omega specific dts(i) file(s) which provide the uart register addresses, and probably some other files I don't know about, the uarts become alive.

    Some work could be done to modify the serial driver to get the highspeed feature to work, but its probably not worth the trouble. Probably better not to mess with something that works, and has been reliably working for a lot of years.

    I now have what I need to get up to 600000 baud (I have been running at 460800 baud for a day or so straight without errors). Simply writing directly to the uart registers is a good solution (for me at least). There just needs to be a way to write to those registers.

    I have written a devmem program in C now that I have the cross-compile all setup. It can run multiple reads/writes from the command line arguments. The following is in my uart script which sets up the highspeed registers when needed (>115200)-

    ./devmem w 0xd24 3 #highspeed
    ./devmem s 0xd0c 0x80 w 0xd00 1 w 0xd04 0 c 0xd0c 0x80 #DLH:DLL
    ./devmem w 0xd28 $sc w 0xd2c $sp #sample count, sample period

    Busybox also has a devmem option (not enabled in default Omega config), but only allows one read/write per invocation.

    If anyone wants the devmem.c source or the devmem binary, I can provide a github link to the files. Maybe its an unusual requirement to be able to set hardware registers directly, but is also handy to view registers and see how they match up to the datasheet.



  • @C-V007 said in Serial ports hi-speed (>115200):

    No surprise, I can ramble on without making a lot of sense.

    You are very polite :) Unfortunately I don't know Go at all :(.

    This is the situation with the official driver for one who don't want to write HW registers directly.
    Most probably you have already known this (better than me ;-).

    Omega2+ FW v0.2.2 b200, device UART1 (ttyS1) - setting for example with stty or screen.

    HIGHSPEED = 0x00, SAMPLE_COUNT = 0x00, SAMPLE_POINT = 0xFF
    0_1543237266922_UART1_high_speed.png

    Notes:
    The divisor is '0x00 0x15' after Power ON but the system change it to the "default" '0x01 0x04' (9600 bps) when the "test" script starts (echo "message" > /dev/ttyS1).
    The 'real speed' is calculated - I'll measure it one day... maybe.


    @C-V007 wrote:

    If anyone wants the devmem.c source or the devmem binary, I can provide a github link to the files.

    I am interested.

    You might be interested to know
    gfiber / vendor / opensource / toolbox / e786d4e7c7e43e2a252a6b1156718802605aaf53 / . / devmem.c



  • source-
    https://github.com/cv007/Omega2_MP3/blob/master/devmem.c
    binary-
    https://github.com/cv007/Omega2_MP3/blob/master/devmem

    I wanted to be able to do a number of writes in one command and also have a bitset/bitclear feature, so wrote my own instead of trying to use what is already out there. The original Go version worked fine but ~1.4MB is a lot of space to give up for my Omega2. The C version is 14kb.



  • @C-V007 Thank you very much.
    Sorry, I don't understand the 'bit' line - could you explain it please.
    Forget that 'bit' line please. It seems I can read line by line... horizontally only. 8-(



  • One more thing :)

    I can actually get 1,000,000 baud working. The problem was the error from the microcontroller was getting too high even in its high speed mode as it just did not have the resolution for super high baud rates (the micro only runs at 24Mhz).

    When the baud divisor value on the micro gets into the single digits the step between each digit is quite large in terms of baud rate, so just happened to get lucky at 430600 and 600000 as they coincided with low error values. I had not realized at the time why these worked, but it is obvious now.

    So am happy with 1MBaud. Quite a bump up from 115200 I would say.


Log in to reply
 

Looks like your connection to Community was lost, please wait while we try to reconnect.