Commit 3c12ddde authored by Ille, Ondrej, Ing.'s avatar Ille, Ondrej, Ing.
Browse files

Merge branch '176-weird-behavior-on-tx-with-errors' into 'master'

Resolve "TXBHCI is triggered on wrong state transitions"

Closes #176

See merge request illeondr/CAN_FD_IP_Core!140
parents 0b4e6ad4 2c9e0c07
...@@ -5285,7 +5285,7 @@ BSI Bit-rate shifted interrupt ...@@ -5285,7 +5285,7 @@ BSI Bit-rate shifted interrupt
RBNEI Receive buffer not empty Interrupt. Clearing this interrupt and not reading out content of RX Buffer via RX_DATA will re-activate the interrupt. RBNEI Receive buffer not empty Interrupt. Clearing this interrupt and not reading out content of RX Buffer via RX_DATA will re-activate the interrupt.
\end_layout \end_layout
\begin_layout Description \begin_layout Description
TXBHCI TX Buffer HW command interrupt. Anytime TX Buffer receives HW command from CAN Core, this interrupt will be acivated. TXBHCI TX Buffer HW command interrupt. Anytime TX Buffer receives HW command from CAN Core which changes TXT Buffer state to TX Ok, Error or Aborted, this interrupt will be acivated.
\end_layout \end_layout
\begin_layout Standard \begin_layout Standard
\begin_inset VSpace bigskip \begin_inset VSpace bigskip
......
*.o *.o
*.d *.d
/test /test
/regtest
*.das *.das
.*.cmd .*.cmd
.tmp_versions .tmp_versions
......
SRCS := ctu_can_fd_hw.c ctu_can_fd_linux_defs.c ctu_can_fd_userspace.cpp SRCS := ctu_can_fd_hw.c ctu_can_fd_linux_defs.c userspace_utils.cpp
OBJS := $(addsuffix .o,$(SRCS)) OBJS := $(addsuffix .o,$(SRCS))
DEPS := $(addsuffix .d,$(SRCS)) DEPS := $(wildcard *.d)
P := arm-linux-gnueabihf- P := arm-linux-gnueabihf-
...@@ -12,13 +12,15 @@ CFLAGS := $(XFLAGS) -Werror=implicit-function-declaration ...@@ -12,13 +12,15 @@ CFLAGS := $(XFLAGS) -Werror=implicit-function-declaration
CXXFLAGS := $(XFLAGS) CXXFLAGS := $(XFLAGS)
#LDFLAGS := -fuse-ld=gold #LDFLAGS := -fuse-ld=gold
all: test all: test regtest
ifeq ($(shell hostname),hathi) ifeq ($(shell hostname),hathi)
cp ./test /srv/nfs4/debian-armhf-devel/ cp ./test ./regtest /srv/nfs4/debian-armhf-devel/
endif endif
test: $(OBJS) test: $(OBJS) ctu_can_fd_userspace.cpp.o
$(CXX) $(XFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
regtest: $(OBJS) regtest.cpp.o
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
%.c.o: %.c %.c.o: %.c
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@ $(CC) $(CFLAGS) -MMD -MP -c $< -o $@
%.cpp.o: %.cpp %.cpp.o: %.cpp
...@@ -26,6 +28,6 @@ test: $(OBJS) ...@@ -26,6 +28,6 @@ test: $(OBJS)
.PHONY: all clean .PHONY: all clean
clean: clean:
-rm test $(OBJS) $(DEPS) -rm test *.o $(DEPS)
-include $(DEPS) -include $(DEPS)
...@@ -369,11 +369,14 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s ...@@ -369,11 +369,14 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s
struct can_frame *cf; struct can_frame *cf;
struct sk_buff *skb; struct sk_buff *skb;
struct can_berr_counter berr; struct can_berr_counter berr;
netdev_info(ndev, "ctucan_err_interrupt");
ctu_can_fd_read_err_ctrs(&priv->p, &berr);
netdev_info(ndev, "ctucan_err_interrupt: ISR = 0x%08x, rxerr %d, txerr %d",
isr.u32, berr.rxerr, berr.txerr);
skb = alloc_can_err_skb(ndev, &cf); skb = alloc_can_err_skb(ndev, &cf);
ctu_can_fd_read_err_ctrs(&priv->p, &berr);
/* /*
* EWI: error warning * EWI: error warning
* DOI: RX overrun * DOI: RX overrun
...@@ -384,14 +387,17 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s ...@@ -384,14 +387,17 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s
if (isr.s.epi) { if (isr.s.epi) {
/* error passive or bus off */ /* error passive or bus off */
enum can_state state = ctu_can_fd_read_error_state(&priv->p); enum can_state state = ctu_can_fd_read_error_state(&priv->p);
netdev_info(ndev, " epi: state = %u", state);
priv->can.state = state; priv->can.state = state;
if (state == CAN_STATE_BUS_OFF) { if (state == CAN_STATE_BUS_OFF) {
priv->can.can_stats.bus_off++; priv->can.can_stats.bus_off++;
netdev_info(ndev, " bus_off");
can_bus_off(ndev); can_bus_off(ndev);
if (skb) if (skb)
cf->can_id |= CAN_ERR_BUSOFF; cf->can_id |= CAN_ERR_BUSOFF;
} else if (state == CAN_STATE_ERROR_PASSIVE) { } else if (state == CAN_STATE_ERROR_PASSIVE) {
priv->can.can_stats.error_passive++; priv->can.can_stats.error_passive++;
netdev_info(ndev, " error_passive");
if (skb) { if (skb) {
cf->can_id |= CAN_ERR_CRTL; cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = (berr.rxerr > 127) ? cf->data[1] = (berr.rxerr > 127) ?
...@@ -400,11 +406,18 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s ...@@ -400,11 +406,18 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s
cf->data[6] = berr.txerr; cf->data[6] = berr.txerr;
cf->data[7] = berr.rxerr; cf->data[7] = berr.rxerr;
} }
} else if (state == CAN_STATE_ERROR_WARNING) {
netdev_warn(ndev, " error_warning, but ISR[EPI] was set! (HW bug?)");
goto err_warning;
} else {
netdev_warn(ndev, " unhandled error state!");
} }
} else if (isr.s.ei) { } else if (isr.s.ei) {
err_warning:
/* error warning */ /* error warning */
priv->can.state = CAN_STATE_ERROR_WARNING; priv->can.state = CAN_STATE_ERROR_WARNING;
priv->can.can_stats.error_warning++; priv->can.can_stats.error_warning++;
netdev_info(ndev, " error_warning");
if (skb) { if (skb) {
cf->can_id |= CAN_ERR_CRTL; cf->can_id |= CAN_ERR_CRTL;
cf->data[1] |= (berr.txerr > berr.rxerr) ? cf->data[1] |= (berr.txerr > berr.rxerr) ?
...@@ -417,6 +430,7 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s ...@@ -417,6 +430,7 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s
/* Check for Arbitration Lost interrupt */ /* Check for Arbitration Lost interrupt */
if (isr.s.ali) { if (isr.s.ali) {
netdev_info(ndev, " arbitration lost");
priv->can.can_stats.arbitration_lost++; priv->can.can_stats.arbitration_lost++;
if (skb) { if (skb) {
cf->can_id |= CAN_ERR_LOSTARB; cf->can_id |= CAN_ERR_LOSTARB;
...@@ -426,6 +440,7 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s ...@@ -426,6 +440,7 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s
/* Check for RX FIFO Overflow interrupt */ /* Check for RX FIFO Overflow interrupt */
if (isr.s.doi) { if (isr.s.doi) {
netdev_info(ndev, " doi (rx fifo overflow)");
stats->rx_over_errors++; stats->rx_over_errors++;
stats->rx_errors++; stats->rx_errors++;
if (skb) { if (skb) {
...@@ -436,6 +451,7 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s ...@@ -436,6 +451,7 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s
/* Check for Bus Error interrupt */ /* Check for Bus Error interrupt */
if (isr.s.bei) { if (isr.s.bei) {
netdev_info(ndev, " bus error");
priv->can.can_stats.bus_error++; priv->can.can_stats.bus_error++;
stats->tx_errors++; // TODO: really? stats->tx_errors++; // TODO: really?
if (skb) { if (skb) {
......
extern "C" { #include "userspace_utils.h"
#include "ctu_can_fd_linux_defs.h"
#include "ctu_can_fd_hw.h"
}
#undef abs
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <inttypes.h>
#include <err.h>
//#include "clara.hpp"
#include <iostream> #include <iostream>
#define CANFD_ADDR_BASE 0x43C30000
#define CANFD_ADDR_RANGE 0x10000
static const char *memdev = "/dev/mem";
static int mem_fd = -1;
static void mem_open()
{
mem_fd = open(memdev, O_RDWR|O_SYNC);
if (mem_fd < 0) {
err(1, "open memory device");
}
}
void *mem_map(unsigned long mem_start, unsigned long mem_length)
{
unsigned long pagesize, mem_window_size;
void *mm, *mem;
//pagesize = getpagesize();
pagesize = sysconf(_SC_PAGESIZE);
mem_window_size = ((mem_start & (pagesize-1)) + mem_length + pagesize-1) & ~(pagesize-1);
mm = mmap(NULL, mem_window_size, PROT_WRITE|PROT_READ,
MAP_SHARED, mem_fd, mem_start & ~(pagesize-1));
mem = (char*)mm + (mem_start & (pagesize-1));
if (mm == MAP_FAILED) {
err(1, "mmap");
return NULL;
}
fprintf(stderr, "mmap 0x%lx -> %p\n",mem_start,mem);
return mem;
}
unsigned ctu_can_fd_read8(struct ctucanfd_priv *priv, enum ctu_can_fd_regs reg) {
return priv->read_reg(priv, (enum ctu_can_fd_regs)(reg & ~3)) >> (8 * (reg & 3));
}
unsigned ctu_can_fd_read16(struct ctucanfd_priv *priv, enum ctu_can_fd_regs reg) {
return priv->read_reg(priv, (enum ctu_can_fd_regs)(reg & ~1)) >> (8 * (reg & 1));
}
/*
void ctu_can_fd_write8(struct ctucanfd_priv *priv, enum ctu_can_fd_regs reg, uint8_t val) {
iowrite8(val, (uint8_t*)priv->mem_base + reg);
}
void ctu_can_fd_write16(struct ctucanfd_priv *priv, enum ctu_can_fd_regs reg, uint16_t val) {
iowrite16(val, (uint8_t*)priv->mem_base + reg);
}*/
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
...@@ -94,28 +29,7 @@ int main(int argc, char *argv[]) ...@@ -94,28 +29,7 @@ int main(int argc, char *argv[])
return 0; return 0;
} }
} }
/*
using namespace clara;
auto cli = //Opt(addr_base, "addr_base")
// ["-A"]["--addr_base"]("CAN FD registers base address")
Opt(ifc, "ifc") ["-i"]("CAN FD interface number")
| Opt(do_transmit)
["-t"]("Do transmit")
| Help(do_showhelp)
;
std::cout << "IFC:" << ifc << std::endl;
auto result = cli.parse(Args(argc, argv));
if (!result) {
std::cerr << "Error in command line: " << result.errorMessage() << std::endl;
exit(1);
}
if (do_showhelp) {
std::cout << cli;
return 0;
}
*/
static const uint32_t addrs[] = {0x43C30000, 0x43C70000}; static const uint32_t addrs[] = {0x43C30000, 0x43C70000};
if (ifc >= 2) { if (ifc >= 2) {
std::cerr << "Err: ifc number must be 0 or 1.\n"; std::cerr << "Err: ifc number must be 0 or 1.\n";
...@@ -123,18 +37,9 @@ int main(int argc, char *argv[]) ...@@ -123,18 +37,9 @@ int main(int argc, char *argv[])
} }
addr_base = addrs[ifc]; addr_base = addrs[ifc];
mem_open(); struct ctucanfd_priv *priv = ctucanfd_init(addr_base);
volatile void * const base = mem_map(addr_base, CANFD_ADDR_RANGE);
struct ctucanfd_priv _p, *priv = &_p;
int res; int res;
priv->mem_base = base;
priv->read_reg = ctu_can_fd_read32;
priv->write_reg = ctu_can_fd_write32;
union ctu_can_fd_device_id_version reg; union ctu_can_fd_device_id_version reg;
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_DEVICE_ID); reg.u32 = priv->read_reg(priv, CTU_CAN_FD_DEVICE_ID);
......
#include "userspace_utils.h"
#include <stdio.h>
#include <assert.h>
#include <stdint.h>
#include <iostream>
/*
Adapted from APB unit test.
Should check that the core is tied to CPU correctly and register access works.
Usage: ./regtest -a 0xAAAAAAAA, where AAAAAAAA is the IP memory map begin
*/
uint32_t s_apb_prdata;
struct ctucanfd_priv *priv;
unsigned error = 0;
void CHECK(uint32_t expected, const char *msg)
{
if (s_apb_prdata != expected) {
printf("%s: expected 0x%08x, got 0x%08x", msg, expected, s_apb_prdata);
error++;
}
}
void apb_write(uint32_t reg, uint32_t value, uint8_t be)
{
enum {
U8, U16, U32
} access;
int offset;
switch (be)
{
case 0b0001: access = U8; offset = 0; break;
case 0b0010: access = U8; offset = 1; break;
case 0b0100: access = U8; offset = 2; break;
case 0b1000: access = U8; offset = 3; break;
case 0b0011: access = U16; offset = 0; break;
case 0b1100: access = U16; offset = 2; break;
case 0b1111: access = U32; offset = 0; break;
default:
assert(!"invalid byte enable");
}
uint8_t * addr = (uint8_t*)priv->mem_base + reg;
switch (access)
{
case U8: iowrite8(value, addr+offset); break;
case U16: iowrite16(value, addr+offset); break;
case U32: iowrite32(value, addr+offset); break;
}
}
void apb_read(uint32_t reg)
{
uint8_t * addr = (uint8_t*)priv->mem_base + reg;
s_apb_prdata = ioread32(addr);
}
void apb_test_pattern(uint32_t reg, uint32_t data)
{
apb_write(reg, data, 0b1111);
apb_read(reg);
if (s_apb_prdata != data) {
printf("pattern 0x%08x mismatch: got 0x%08x", data, s_apb_prdata);
error++;
}
}
int main(int argc, char *argv[])
{
uint32_t addr_base = 0;
int c;
char *e;
const char *progname = argv[0];
while ((c = getopt(argc, argv, "a:th")) != -1) {
switch (c) {
case 'a':
addr_base = strtoul(optarg, &e, 0);
if (*e != '\0')
err(1, "-i expects a number");
break;
case 'h':
printf("Usage: %s [-a ifc_addr]\n\n",
progname
);
return 0;
}
}
if (addr_base < 0x40000000 || addr_base > 0xBFFFFFFF) {
std::cerr << "Err: ifc address must lie in AXI_GP0 or AXI_GP1 port range (on Zynq).\n";
exit(1);
}
if (addr_base & 0xFFF) {
std::cerr << "Err: ifc address must be aligned to at least 4096 bytes.\n";
exit(1);
}
priv = ctucanfd_init(addr_base);
union ctu_can_fd_device_id_version reg;
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_DEVICE_ID);
printf("DevID: 0x%08x, should be 0x%08x\n", reg.s.device_id, CTU_CAN_FD_ID);
apb_read(CTU_CAN_FD_DEVICE_ID);
CHECK(0x0201CAFD, "CAN ID reg mismatch (just after HW reset)");
apb_write(CTU_CAN_FD_BTR, 0xFFFFFFFF, 0b1111);
apb_read(CTU_CAN_FD_DEVICE_ID);
CHECK(0x0201CAFD, "CAN ID reg mismatch");
apb_read(CTU_CAN_FD_BTR);
CHECK(0xFFFFFFFF, "readback mismatch");
apb_write(CTU_CAN_FD_BTR, 0x00000000, 0b0011);
apb_read(CTU_CAN_FD_BTR);
CHECK(0xFFFF0000, "write low word: readback mismatch");
apb_write(CTU_CAN_FD_BTR, 0xFFFFFFFF, 0b1111);
apb_write(CTU_CAN_FD_BTR, 0x00000000, 0b1100);
apb_read(CTU_CAN_FD_BTR);
CHECK(0x0000FFFF, "write high word: readback mismatch");
apb_test_pattern(CTU_CAN_FD_BTR, 0xAAAAAAAA);
apb_test_pattern(CTU_CAN_FD_BTR, 0x55555555);
apb_test_pattern(CTU_CAN_FD_BTR, 0x92492492);
apb_test_pattern(CTU_CAN_FD_BTR, 0x49249249);
apb_test_pattern(CTU_CAN_FD_BTR, 0x24924924);
apb_write(CTU_CAN_FD_BTR, 0x87654321, 0b1111);
apb_read(CTU_CAN_FD_BTR);
CHECK(0x87654321, "readback mismatch");
apb_write(CTU_CAN_FD_BTR, 0x000055aa, 0b0011);
apb_read(CTU_CAN_FD_BTR);
CHECK(0x876555aa, "write low word: readback mismatch");
if (error)
printf("There were %u errors.\n", error);
else
printf("Success!\n");
return error ? 1 : 0;
}
#include "userspace_utils.h"
#include <iostream>
static const char * const memdev = "/dev/mem";
static int mem_fd = -1;
#define CANFD_ADDR_RANGE 4096
static void mem_open()
{
if (mem_fd >= 0)
return;
mem_fd = open(memdev, O_RDWR|O_SYNC);
if (mem_fd < 0) {
err(1, "open memory device");
}
}
static void *mem_map(unsigned long mem_start, unsigned long mem_length)
{
unsigned long pagesize, mem_window_size;
void *mm, *mem;
//pagesize = getpagesize();
pagesize = sysconf(_SC_PAGESIZE);
mem_window_size = ((mem_start & (pagesize-1)) + mem_length + pagesize-1) & ~(pagesize-1);
mm = mmap(NULL, mem_window_size, PROT_WRITE|PROT_READ,
MAP_SHARED, mem_fd, mem_start & ~(pagesize-1));
mem = (char*)mm + (mem_start & (pagesize-1));
if (mm == MAP_FAILED) {
err(1, "mmap");
return NULL;
}
fprintf(stderr, "mmap 0x%lx -> %p\n",mem_start,mem);
return mem;
}
unsigned ctu_can_fd_read8(struct ctucanfd_priv *priv, enum ctu_can_fd_regs reg) {
return priv->read_reg(priv, (enum ctu_can_fd_regs)(reg & ~3)) >> (8 * (reg & 3));
}
unsigned ctu_can_fd_read16(struct ctucanfd_priv *priv, enum ctu_can_fd_regs reg) {
return priv->read_reg(priv, (enum ctu_can_fd_regs)(reg & ~1)) >> (8 * (reg & 1));
}
/*
void ctu_can_fd_write8(struct ctucanfd_priv *priv, enum ctu_can_fd_regs reg, uint8_t val) {
iowrite8(val, (uint8_t*)priv->mem_base + reg);
}
void ctu_can_fd_write16(struct ctucanfd_priv *priv, enum ctu_can_fd_regs reg, uint16_t val) {
iowrite16(val, (uint8_t*)priv->mem_base + reg);
}*/
struct ctucanfd_priv* ctucanfd_init(uint32_t addr)
{
mem_open();
volatile void * const base = mem_map(addr, CANFD_ADDR_RANGE);
struct ctucanfd_priv *priv = new ctucanfd_priv;
memset(priv, 0, sizeof(*priv));
priv->mem_base = base;
priv->read_reg = ctu_can_fd_read32;
priv->write_reg = ctu_can_fd_write32;
// will leak memory, but who cares, this is just a prototype testing tool
return priv;
}
#pragma once
extern "C" {
#include "ctu_can_fd_linux_defs.h"
#include "ctu_can_fd_hw.h"
}
#undef abs
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <inttypes.h>
#include <err.h>
struct ctucanfd_priv* ctucanfd_init(uint32_t addr);
unsigned ctu_can_fd_read8(struct ctucanfd_priv *priv, enum ctu_can_fd_regs reg);
unsigned ctu_can_fd_read16(struct ctucanfd_priv *priv, enum ctu_can_fd_regs reg);
...@@ -60,7 +60,9 @@ if __name__ == '__main__': ...@@ -60,7 +60,9 @@ if __name__ == '__main__':
print("** Generating CAN FD register map") print("** Generating CAN FD register map")
print(80 * "*") print(80 * "*")
pythonAlias = "python3.5" pythonVersion = sys.version.split('.')
pythonAlias = "python" + pythonVersion[0] + "." + pythonVersion[1]
print("\n Python version is: %s \n" % pythonAlias)
if (str_arg_to_bool(args.updVHDL)): if (str_arg_to_bool(args.updVHDL)):
print("Generating CAN FD memory registers VHDL package...\n") print("Generating CAN FD memory registers VHDL package...\n")
......
...@@ -687,7 +687,7 @@ ...@@ -687,7 +687,7 @@
<ipxact:field> <ipxact:field>
<ipxact:name>TXBHCI</ipxact:name> <ipxact:name>TXBHCI</ipxact:name>
<ipxact:displayName>TXBHCI</ipxact:displayName> <ipxact:displayName>TXBHCI</ipxact:displayName>
<ipxact:description>TX Buffer HW command interrupt. Anytime TX Buffer receives HW command from CAN Core, this interrupt will be acivated.</ipxact:description> <ipxact:description>TX Buffer HW command interrupt. Anytime TX Buffer receives HW command from CAN Core which changes TXT Buffer state to TX Ok, Error or Aborted, this interrupt will be acivated.</ipxact:description>
<ipxact:bitOffset>11</ipxact:bitOffset> <ipxact:bitOffset>11</ipxact:bitOffset>
<ipxact:resets> <ipxact:resets>
<ipxact:reset> <ipxact:reset>
......