This is an automated email from Gerrit.

bunnie ([email protected]) just uploaded a new patch set to Gerrit, 
which you can find at http://openocd.zylin.com/4721

-- gerrit

commit 9ab8558e5e41b46f5b125e5739566fd587fb35ab
Author: bunnie <[email protected]>
Date:   Tue Oct 16 10:08:51 2018 -0400

    work around bcm2835 gpio hardware synchronization bug
    
        An issue was found on the Raspberry Pi 3B+ (which may apply to other 
models) where the BCM2835 GPIO implementation can cause glitches. It looks a 
bit like this:
    
        https://bunniefoo.com/bunnie/openocd-lone-glitch.png
        https://bunniefoo.com/bunnie/openocd-serial-glitch.png
    
        It seems that the GPIO unit on the BCM2835 runs at a much slower clock 
rate (maybe 125MHz based on the glitch width?) and somehow data committed 
to/from the CPU is not synched correctly. This would manifest in set/clr events 
not executing immediately, and also stale data being stuck on the read path. 
This synch failure could either be due to a cache coherence bug, or it could be 
a clock domain synchronizer that's not quite working right. My guess is it's 
the latter -- the synchronizer may be holding off on passing the very last 
write or read until some arbitrary point later, could be that it misses a bus 
slot or whatever, but this leads to strictly the correct order of data, but the 
time at which the data arrives can be highly variable.
    
        The glitches would happen maybe once every million cycles or so. The 
Xilinx XC7 FPGA is fast enough to actually respond to the glitch and continue 
working if the data levels are constant through the glitched clock, so in many 
cases the glitch would not lead to an improper configuration. However, we'd see 
a fault maybe once every few configurations, and SPI programming absolutely 
would not work with the BCM2835 module (works fine with sysfs).
    
        The fix is to insert a dummy read to the BCM2835 GPIO block before 
doing any read or write transaction. The read seems to flush the synchronizer 
or cache so that things are in a good state for the next operation.
    
        The downside is that the read is slow, this limits the overall bitbang 
rate, but at least it works as intended. I'm able to clock about a 5MHz rate 
now, error-free.
    
    Change-Id: I5548863f96f9b4702d559fd087bcea7e487b2771
    Signed-off-by: bunnie <[email protected]>

diff --git a/src/jtag/drivers/bcm2835gpio.c b/src/jtag/drivers/bcm2835gpio.c
index 38ef163..f8f1885 100644
--- a/src/jtag/drivers/bcm2835gpio.c
+++ b/src/jtag/drivers/bcm2835gpio.c
@@ -91,8 +91,13 @@ static int speed_coeff = 113714;
 static int speed_offset = 28;
 static unsigned int jtag_delay;
 
+/* dummy value for synchronizing bcm2835 GPIO transactions */
+static uint32_t lev;
+
 static bb_value_t bcm2835gpio_read(void)
 {
+       lev = GPIO_LEV; /* dummy read to synchronize bcm2835 gpio block */
+
        return (GPIO_LEV & 1<<tdo_gpio) ? BB_HIGH : BB_LOW;
 }
 
@@ -101,6 +106,7 @@ static int bcm2835gpio_write(int tck, int tms, int tdi)
        uint32_t set = tck<<tck_gpio | tms<<tms_gpio | tdi<<tdi_gpio;
        uint32_t clear = !tck<<tck_gpio | !tms<<tms_gpio | !tdi<<tdi_gpio;
 
+       lev = GPIO_LEV;  /* dummy read to synch bcm2835 gpio block */
        GPIO_SET = set;
        GPIO_CLR = clear;
 
@@ -115,6 +121,7 @@ static int bcm2835gpio_swd_write(int tck, int tms, int tdi)
        uint32_t set = tck<<swclk_gpio | tdi<<swdio_gpio;
        uint32_t clear = !tck<<swclk_gpio | !tdi<<swdio_gpio;
 
+       lev = GPIO_LEV;
        GPIO_SET = set;
        GPIO_CLR = clear;
 
@@ -140,6 +147,7 @@ static int bcm2835gpio_reset(int trst, int srst)
                clear |= srst<<srst_gpio;
        }
 
+       lev = GPIO_LEV;
        GPIO_SET = set;
        GPIO_CLR = clear;
 
@@ -485,6 +493,7 @@ static int bcm2835gpio_init(void)
         */
        INP_GPIO(tdo_gpio);
 
+       lev = GPIO_LEV;
        GPIO_CLR = 1<<tdi_gpio | 1<<tck_gpio | 1<<swdio_gpio | 1<<swclk_gpio;
        GPIO_SET = 1<<tms_gpio;
 
@@ -495,11 +504,13 @@ static int bcm2835gpio_init(void)
        OUT_GPIO(swdio_gpio);
        if (trst_gpio != -1) {
                trst_gpio_mode = MODE_GPIO(trst_gpio);
+               lev = GPIO_LEV;
                GPIO_SET = 1 << trst_gpio;
                OUT_GPIO(trst_gpio);
        }
        if (srst_gpio != -1) {
                srst_gpio_mode = MODE_GPIO(srst_gpio);
+               lev = GPIO_LEV;
                GPIO_SET = 1 << srst_gpio;
                OUT_GPIO(srst_gpio);
        }

-- 


_______________________________________________
OpenOCD-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openocd-devel

Reply via email to