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

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
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
\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
\begin_layout Standard
\begin_inset VSpace bigskip
......
*.o
*.d
/test
/regtest
*.das
.*.cmd
.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))
DEPS := $(addsuffix .d,$(SRCS))
DEPS := $(wildcard *.d)
P := arm-linux-gnueabihf-
......@@ -12,13 +12,15 @@ CFLAGS := $(XFLAGS) -Werror=implicit-function-declaration
CXXFLAGS := $(XFLAGS)
#LDFLAGS := -fuse-ld=gold
all: test
all: test regtest
ifeq ($(shell hostname),hathi)
cp ./test /srv/nfs4/debian-armhf-devel/
cp ./test ./regtest /srv/nfs4/debian-armhf-devel/
endif
test: $(OBJS)
$(CXX) $(XFLAGS) -o $@ $(OBJS) $(LDFLAGS)
test: $(OBJS) ctu_can_fd_userspace.cpp.o
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
regtest: $(OBJS) regtest.cpp.o
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
%.c.o: %.c
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
%.cpp.o: %.cpp
......@@ -26,6 +28,6 @@ test: $(OBJS)
.PHONY: all clean
clean:
-rm test $(OBJS) $(DEPS)
-rm test *.o $(DEPS)
-include $(DEPS)
......@@ -369,11 +369,14 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s
struct can_frame *cf;
struct sk_buff *skb;
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);
ctu_can_fd_read_err_ctrs(&priv->p, &berr);
/*
* EWI: error warning
* DOI: RX overrun
......@@ -384,14 +387,17 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s
if (isr.s.epi) {
/* error passive or bus off */
enum can_state state = ctu_can_fd_read_error_state(&priv->p);
netdev_info(ndev, " epi: state = %u", state);
priv->can.state = state;
if (state == CAN_STATE_BUS_OFF) {
priv->can.can_stats.bus_off++;
netdev_info(ndev, " bus_off");
can_bus_off(ndev);
if (skb)
cf->can_id |= CAN_ERR_BUSOFF;
} else if (state == CAN_STATE_ERROR_PASSIVE) {
priv->can.can_stats.error_passive++;
netdev_info(ndev, " error_passive");
if (skb) {
cf->can_id |= CAN_ERR_CRTL;
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
cf->data[6] = berr.txerr;
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) {
err_warning:
/* error warning */
priv->can.state = CAN_STATE_ERROR_WARNING;
priv->can.can_stats.error_warning++;
netdev_info(ndev, " error_warning");
if (skb) {
cf->can_id |= CAN_ERR_CRTL;
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
/* Check for Arbitration Lost interrupt */
if (isr.s.ali) {
netdev_info(ndev, " arbitration lost");
priv->can.can_stats.arbitration_lost++;
if (skb) {
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
/* Check for RX FIFO Overflow interrupt */
if (isr.s.doi) {
netdev_info(ndev, " doi (rx fifo overflow)");
stats->rx_over_errors++;
stats->rx_errors++;
if (skb) {
......@@ -436,6 +451,7 @@ static void ctucan_err_interrupt(struct net_device *ndev, union ctu_can_fd_int_s
/* Check for Bus Error interrupt */
if (isr.s.bei) {
netdev_info(ndev, " bus error");
priv->can.can_stats.bus_error++;
stats->tx_errors++; // TODO: really?
if (skb) {
......
extern "C" {
#include "ctu_can_fd_linux_defs.h"
#include "ctu_can_fd_hw.h"
}
#include "userspace_utils.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>
#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[])
{
......@@ -94,28 +29,7 @@ int main(int argc, char *argv[])
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};
if (ifc >= 2) {
std::cerr << "Err: ifc number must be 0 or 1.\n";
......@@ -123,18 +37,9 @@ int main(int argc, char *argv[])
}
addr_base = addrs[ifc];
mem_open();
volatile void * const base = mem_map(addr_base, CANFD_ADDR_RANGE);
struct ctucanfd_priv _p, *priv = &_p;
struct ctucanfd_priv *priv = ctucanfd_init(addr_base);
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;
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__':
print("** Generating CAN FD register map")
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)):
print("Generating CAN FD memory registers VHDL package...\n")
......
......@@ -687,7 +687,7 @@
<ipxact:field>
<ipxact:name>TXBHCI</ipxact:name>
<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:resets>
<ipxact:reset>
......
......@@ -68,6 +68,9 @@
-- progress" states.
-- 06.4.2018 Changed output from side of CAN Core to synchronous. Async.
-- output did not allow inferrence of RAM in Altera FPGA.
-- 30.8.2018 Added "txt_hw_cmd_int" output for Interrupt Manager. TXTB HW
-- command Interrupt generated upon move to Done, Failed or
-- Aborted states.
--------------------------------------------------------------------------------
Library ieee;
......@@ -106,6 +109,11 @@ entity txtBuffer is
------------------------------------------------------------------------
signal txtb_state :out txt_fsm_type;
------------------------------------------------------------------------
-- Interrupt Manager
------------------------------------------------------------------------
signal txt_hw_cmd_int :out std_logic;
------------------------------------------------------------------------
-- CAN Core and TX Arbiter Interface
------------------------------------------------------------------------
......@@ -171,6 +179,18 @@ begin
sw_cbs <= '1' when txt_sw_buf_cmd_index(ID) = '1'
else
'0';
-- TXT Buffer HW Command generates interrupt upon transition to
-- Failed, Done and Aborted states!
txt_hw_cmd_int <= '1' when (hw_cbs = '1') and ((txt_hw_cmd.failed = '1') or
(txt_hw_cmd.valid = '1') or
((txt_hw_cmd.unlock = '1') and
(buf_fsm = txt_ab_prog)) or
((txt_sw_cmd.set_abt = '1') and
(sw_cbs = '1') and
(buf_fsm = txt_ready)))
else
'0';
-- Connect internal buffer state to output
txtb_state <= buf_fsm;
......@@ -259,7 +279,7 @@ begin
--------------------------------------------------------------------
-- Transmission from buffer is in progress
--------------------------------------------------------------------
--------------------------------------------------------------------
when txt_tx_prog =>
-- Unlock the buffer
......
......@@ -287,6 +287,10 @@ entity CAN_top_level is
-- Hardware commands to TXT Buffer from Protocol control
signal txt_hw_cmd : txt_hw_cmd_type;
-- TXT Buffer HW CMD Interrupt activated on TXT Buffer
signal txt_hw_cmd_int : std_logic_vector(TXT_BUFFER_COUNT - 1
downto 0);
-- Hardware command index set by TX Arbitrator based on the current
-- internal state
signal txt_hw_cmd_index : natural range 0 to TXT_BUFFER_COUNT - 1;
......@@ -597,6 +601,7 @@ begin
txt_sw_buf_cmd_index => txt_buf_cmd_index,
txtb_state => txtb_fsms(i),
txt_hw_cmd => txt_hw_cmd,
txt_hw_cmd_int => txt_hw_cmd_int(i),
txt_hw_cmd_buf_index => txt_hw_cmd_buf_index,
bus_off_start => bus_off_start,
txt_word => txt_word(i),
......@@ -683,7 +688,7 @@ begin
rec_message_valid => rec_message_valid,
rx_full => rx_full,
rx_empty => rx_empty,
txt_hw_cmd => txt_hw_cmd,
txt_hw_cmd_int => txt_hw_cmd_int,
loger_finished => loger_finished,
drv_bus => drv_bus,
int_out => int,
......
......@@ -59,6 +59,10 @@
-- be level based instead of edge based with fixed duration. This
-- is more fitting for SocketCAN implementation.
-- 12.3.2018 Implemented RX Buffer not empty and TX Buffer HW command INT.
-- 30.8.2018 Moved HW command detection logic to TXT Buffer from here.
-- Thus TXT Buffer can properly filter commands, to avoid
-- overflow of interrupts! Replaced "txt_hw_cmd" with
-- "txt_hw_cmd_int" signal.
--------------------------------------------------------------------------------
Library ieee;
......@@ -114,8 +118,9 @@ entity intManager is
-- Recieve buffer is empty
signal rx_empty :in std_logic;
-- HW commands on TXT Buffer
signal txt_hw_cmd :in txt_hw_cmd_type;
-- HW command on TXT Buffers interrupt
signal txt_hw_cmd_int :in std_logic_vector(TXT_BUFFER_COUNT - 1
downto 0);