Commit 2b25fb05 authored by Martin Jeřábek's avatar Martin Jeřábek

Merge branch '106-simple-driver-in-userspace' into 'master'

Resolve "Simple driver in userspace"

Closes #106 and #150

See merge request illeondr/CAN_FD_IP_Core!113
parents 4e916744 6d11bf73
Pipeline #1315 passed with stages
in 3 minutes and 39 seconds
......@@ -20,9 +20,8 @@ build_ip_and_tests:
- triggers # Run by trigger (on merge request)
- web # Run by manual request from web UI
.build_driver:
build_driver:
stage: build
allow_failure: true
only: *only
script:
- cd driver
......
*.o
*.d
/test
*.das
.*.cmd
.tmp_versions
modules.order
Module.symvers
ctucanfd.mod.c
a.out
*.ko
SRCS := ctu_can_fd_hw.c ctu_can_fd_linux_defs.c ctu_can_fd_userspace.cpp
OBJS := $(addsuffix .o,$(SRCS))
DEPS := $(addsuffix .d,$(SRCS))
P := arm-linux-gnueabihf-
CC := $(P)gcc
CXX := $(P)g++
#CC := clang -target armv7a-pc-linux-gnueabi -march=armv7 -mthumb
XFLAGS := -Wall -Wextra -O2 -D__LITTLE_ENDIAN_BITFIELD -mthumb
CFLAGS := $(XFLAGS) -Werror=implicit-function-declaration
CXXFLAGS := $(XFLAGS)
#LDFLAGS := -fuse-ld=gold
all: test
ifeq ($(shell hostname),hathi)
cp ./test /srv/nfs4/debian-armhf-devel/
endif
test: $(OBJS)
$(CXX) $(XFLAGS) -o $@ $(OBJS) $(LDFLAGS)
%.c.o: %.c
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
%.cpp.o: %.cpp
$(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@
.PHONY: all clean
clean:
-rm test $(OBJS) $(DEPS)
-include $(DEPS)
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#include "ctu_can_fd_linux_defs.h"
#include <errno.h>
#include <limits.h>
/* CAN DLC to real data length conversion helpers */
static const u8 dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7,
8, 12, 16, 20, 24, 32, 48, 64};
/* get data length from can_dlc with sanitized can_dlc */
u8 can_dlc2len(u8 can_dlc)
{
return dlc2len[can_dlc & 0x0F];
}
static const u8 len2dlc[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */
9, 9, 9, 9, /* 9 - 12 */
10, 10, 10, 10, /* 13 - 16 */
11, 11, 11, 11, /* 17 - 20 */
12, 12, 12, 12, /* 21 - 24 */
13, 13, 13, 13, 13, 13, 13, 13, /* 25 - 32 */
14, 14, 14, 14, 14, 14, 14, 14, /* 33 - 40 */
14, 14, 14, 14, 14, 14, 14, 14, /* 41 - 48 */
15, 15, 15, 15, 15, 15, 15, 15, /* 49 - 56 */
15, 15, 15, 15, 15, 15, 15, 15}; /* 57 - 64 */
/* map the sanitized data length to an appropriate data length code */
u8 can_len2dlc(u8 len)
{
if (unlikely(len > 64))
return 0xF;
return len2dlc[len];
}
#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
#define CAN_CALC_SYNC_SEG 1
/*
* Bit-timing calculation derived from:
*
* Code based on LinCAN sources and H8S2638 project
* Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz
* Copyright 2005 Stanislav Marek
* email: pisa@cmp.felk.cvut.cz
*
* Calculates proper bit-timing parameters for a specified bit-rate
* and sample-point, which can then be used to set the bit-timing
* registers of the CAN controller. You can find more information
* in the header file linux/can/netlink.h.
*/
static int can_update_sample_point(const struct can_bittiming_const *btc,
unsigned int sample_point_nominal, unsigned int tseg,
unsigned int *tseg1_ptr, unsigned int *tseg2_ptr,
unsigned int *sample_point_error_ptr)
{
unsigned int sample_point_error, best_sample_point_error = UINT_MAX;
unsigned int sample_point, best_sample_point = 0;
unsigned int tseg1, tseg2;
int i;
for (i = 0; i <= 1; i++) {
tseg2 = tseg + CAN_CALC_SYNC_SEG - (sample_point_nominal * (tseg + CAN_CALC_SYNC_SEG)) / 1000 - i;
tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max);
tseg1 = tseg - tseg2;
if (tseg1 > btc->tseg1_max) {
tseg1 = btc->tseg1_max;
tseg2 = tseg - tseg1;
}
sample_point = 1000 * (tseg + CAN_CALC_SYNC_SEG - tseg2) / (tseg + CAN_CALC_SYNC_SEG);
sample_point_error = abs(sample_point_nominal - sample_point);
if ((sample_point <= sample_point_nominal) && (sample_point_error < best_sample_point_error)) {
best_sample_point = sample_point;
best_sample_point_error = sample_point_error;
*tseg1_ptr = tseg1;
*tseg2_ptr = tseg2;
}
}
if (sample_point_error_ptr)
*sample_point_error_ptr = best_sample_point_error;
return best_sample_point;
}
static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
const struct can_bittiming_const *btc)
{
struct can_priv *priv = netdev_priv(dev);
unsigned int bitrate; /* current bitrate */
unsigned int bitrate_error; /* difference between current and nominal value */
unsigned int best_bitrate_error = UINT_MAX;
unsigned int sample_point_error; /* difference between current and nominal value */
unsigned int best_sample_point_error = UINT_MAX;
unsigned int sample_point_nominal; /* nominal sample point */
unsigned int best_tseg = 0; /* current best value for tseg */
unsigned int best_brp = 0; /* current best value for brp */
unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0;
u64 v64;
/* Use CiA recommended sample points */
if (bt->sample_point) {
sample_point_nominal = bt->sample_point;
} else {
if (bt->bitrate > 800000)
sample_point_nominal = 750;
else if (bt->bitrate > 500000)
sample_point_nominal = 800;
else
sample_point_nominal = 875;
}
/* tseg even = round down, odd = round up */
for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) {
tsegall = CAN_CALC_SYNC_SEG + tseg / 2;
/* Compute all possible tseg choices (tseg=tseg1+tseg2) */
brp = priv->clock.freq / (tsegall * bt->bitrate) + tseg % 2;
/* choose brp step which is possible in system */
brp = (brp / btc->brp_inc) * btc->brp_inc;
if ((brp < btc->brp_min) || (brp > btc->brp_max))
continue;
bitrate = priv->clock.freq / (brp * tsegall);
bitrate_error = abs(bt->bitrate - bitrate);
/* tseg brp biterror */
if (bitrate_error > best_bitrate_error)
continue;
/* reset sample point error if we have a better bitrate */
if (bitrate_error < best_bitrate_error)
best_sample_point_error = UINT_MAX;
can_update_sample_point(btc, sample_point_nominal, tseg / 2, &tseg1, &tseg2, &sample_point_error);
if (sample_point_error > best_sample_point_error)
continue;
best_sample_point_error = sample_point_error;
best_bitrate_error = bitrate_error;
best_tseg = tseg / 2;
best_brp = brp;
if (bitrate_error == 0 && sample_point_error == 0)
break;
}
if (best_bitrate_error) {
/* Error in one-tenth of a percent */
v64 = (u64)best_bitrate_error * 1000;
do_div(v64, bt->bitrate);
bitrate_error = (u32)v64;
if (bitrate_error > CAN_CALC_MAX_ERROR) {
netdev_err(dev,
"bitrate error %d.%d%% too high\n",
bitrate_error / 10, bitrate_error % 10);
return -EDOM;
}
netdev_warn(dev, "bitrate error %d.%d%%\n",
bitrate_error / 10, bitrate_error % 10);
}
/* real sample point */
bt->sample_point = can_update_sample_point(btc, sample_point_nominal, best_tseg,
&tseg1, &tseg2, NULL);
v64 = (u64)best_brp * 1000 * 1000 * 1000;
do_div(v64, priv->clock.freq);
bt->tq = (u32)v64;
bt->prop_seg = tseg1 / 2;
bt->phase_seg1 = tseg1 - bt->prop_seg;
bt->phase_seg2 = tseg2;
/* check for sjw user settings */
if (!bt->sjw || !btc->sjw_max) {
bt->sjw = 1;
} else {
/* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */
if (bt->sjw > btc->sjw_max)
bt->sjw = btc->sjw_max;
/* bt->sjw must not be higher than tseg2 */
if (tseg2 < bt->sjw)
bt->sjw = tseg2;
}
bt->brp = best_brp;
/* real bitrate */
bt->bitrate = priv->clock.freq / (bt->brp * (CAN_CALC_SYNC_SEG + tseg1 + tseg2));
return 0;
}
/*
* Checks the validity of the specified bit-timing parameters prop_seg,
* phase_seg1, phase_seg2 and sjw and tries to determine the bitrate
* prescaler value brp. You can find more information in the header
* file linux/can/netlink.h.
*/
static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt,
const struct can_bittiming_const *btc)
{
struct can_priv *priv = netdev_priv(dev);
int tseg1, alltseg;
u64 brp64;
tseg1 = bt->prop_seg + bt->phase_seg1;
if (!bt->sjw)
bt->sjw = 1;
if (bt->sjw > btc->sjw_max ||
tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max ||
bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max)
return -ERANGE;
brp64 = (u64)priv->clock.freq * (u64)bt->tq;
if (btc->brp_inc > 1)
do_div(brp64, btc->brp_inc);
brp64 += 500000000UL - 1;
do_div(brp64, 1000000000UL); /* the practicable BRP */
if (btc->brp_inc > 1)
brp64 *= btc->brp_inc;
bt->brp = (u32)brp64;
if (bt->brp < btc->brp_min || bt->brp > btc->brp_max)
return -EINVAL;
alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1;
bt->bitrate = priv->clock.freq / (bt->brp * alltseg);
bt->sample_point = ((tseg1 + 1) * 1000) / alltseg;
return 0;
}
/* Checks the validity of predefined bitrate settings */
static int can_validate_bitrate(struct net_device *dev, struct can_bittiming *bt,
const u32 *bitrate_const,
const unsigned int bitrate_const_cnt)
{
struct can_priv *priv = netdev_priv(dev);
unsigned int i;
for (i = 0; i < bitrate_const_cnt; i++) {
if (bt->bitrate == bitrate_const[i])
break;
}
if (i >= priv->bitrate_const_cnt)
return -EINVAL;
return 0;
}
int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt,
const struct can_bittiming_const *btc,
const u32 *bitrate_const,
const unsigned int bitrate_const_cnt)
{
int err;
/*
* Depending on the given can_bittiming parameter structure the CAN
* timing parameters are calculated based on the provided bitrate OR
* alternatively the CAN timing parameters (tq, prop_seg, etc.) are
* provided directly which are then checked and fixed up.
*/
if (!bt->tq && bt->bitrate && btc)
err = can_calc_bittiming(dev, bt, btc);
else if (bt->tq && !bt->bitrate && btc)
err = can_fixup_bittiming(dev, bt, btc);
else if (!bt->tq && bt->bitrate && bitrate_const)
err = can_validate_bitrate(dev, bt, bitrate_const,
bitrate_const_cnt);
else
err = -EINVAL;
return err;
}
......@@ -35,6 +35,95 @@
#ifndef __CTU_CAN_FD_LINUX_DEFS__
#define __CTU_CAN_FD_LINUX_DEFS__
#include <stdint.h>
#include <stdio.h>
#include <stddef.h>
//#include <linux/types.h>
#include <linux/socket.h>
#include <arpa/inet.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define __iomem volatile
typedef uint8_t __u8;
typedef uint16_t __u16;
typedef uint32_t __u32;
typedef uint64_t __u64;
typedef int8_t __s8;
typedef int16_t __s16;
typedef int32_t __s32;
typedef int64_t __s64;
typedef __u8 u8;
typedef __u16 u16;
typedef __u32 u32;
typedef __u64 u64;
typedef __s8 s8;
typedef __s16 s16;
typedef __s32 s32;
typedef __s64 s64;
#ifndef __cplusplus
typedef _Bool bool;
enum {
false = 0,
true = 1
};
#endif
#define __WARN_printf printf
#ifndef WARN
#define WARN(condition, format...) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN_printf(format); \
unlikely(__ret_warn_on); \
})
#endif
/*
// from include/uapi/linux/types.h
#define __bitwise
typedef __u16 __bitwise __le16;
typedef __u16 __bitwise __be16;
typedef __u32 __bitwise __le32;
typedef __u32 __bitwise __be32;
typedef __u64 __bitwise __le64;
typedef __u64 __bitwise __be64;
typedef __u16 __bitwise __sum16;
typedef __u32 __bitwise __wsum;
*/
__attribute__((noinline))
static inline void iowrite32(u32 value, void *addr) {*(volatile u32*)addr = value;}
__attribute__((noinline))
static inline void iowrite16(u16 value, void *addr) {*(volatile u16*)addr = value;}
__attribute__((noinline))
static inline void iowrite8(u8 value, void *addr) {*(volatile u8*)addr = value;}
__attribute__((noinline))
static inline u32 ioread32(const void *addr) {return *(const volatile u32*)addr;}
__attribute__((noinline))
static inline u16 ioread16(const void *addr) {return *(const volatile u16*)addr;}
__attribute__((noinline))
static inline u8 ioread8(const void *addr) {return *(const volatile u8*)addr;}
static inline u32 cpu_to_be32(u32 v) {return htonl(v);}
static inline u32 be32_to_cpu(u32 v) {return ntohl(v);}
__attribute__((noinline))
static inline void iowrite32be(u32 value, void *addr) {*(volatile u32*)addr = cpu_to_be32(value);}
__attribute__((noinline))
static inline u32 ioread32be(const void *addr) {return be32_to_cpu(*(const volatile u32*)addr);}
/* CAN DLC to real data length conversion helpers */
u8 can_dlc2len(u8 can_dlc);
u8 can_len2dlc(u8 len);
/*
* CAN bit-timing parameters
*
......@@ -204,8 +293,6 @@ enum {
#ifndef _UAPI_CAN_H
#define _UAPI_CAN_H
#include <linux/types.h>
#include <linux/socket.h>
......@@ -388,5 +475,83 @@ struct can_filter {
#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
#define CAN_RAW_FILTER_MAX 512 /* maximum number of can_filter set via setsockopt() */
#endif /* _UAPI_CAN_H */
struct can_priv {
struct can_bittiming bittiming, data_bittiming;
const struct can_bittiming_const *bittiming_const,
*data_bittiming_const;
//const u16 *termination_const;
//unsigned int termination_const_cnt;
//u16 termination;
const u32 *bitrate_const;
unsigned int bitrate_const_cnt;
const u32 *data_bitrate_const;
unsigned int data_bitrate_const_cnt;
struct can_clock clock;
};
#endif
struct net_device {
struct can_priv can;
};
#define netdev_priv(nd) (&((nd)->can))
int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt,
const struct can_bittiming_const *btc,
const u32 *bitrate_const,
const unsigned int bitrate_const_cnt);
#define min(a, b) (a < b ? a : b)
#define max(a, b) (a > b ? a : b)
#define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi)
/**
* do_div - returns 2 values: calculate remainder and update new dividend
* @n: pointer to uint64_t dividend (will be updated)
* @base: uint32_t divisor
*
* Summary:
* ``uint32_t remainder = *n % base;``
* ``*n = *n / base;``
*
* Return: (uint32_t)remainder
*
* NOTE: macro parameter @n is evaluated multiple times,
* beware of side effects!
*/
# define do_div(n,base) ({ \
uint32_t __base = (base); \
uint32_t __rem; \
__rem = ((uint64_t)(n)) % __base; \
(n) = ((uint64_t)(n)) / __base; \
__rem; \
})
/**
* abs - return absolute value of an argument
* @x: the value. If it is unsigned type, it is converted to signed type first.
* char is treated as if it was signed (regardless of whether it really is)
* but the macro's return type is preserved as char.
*
* Return: an absolute value of x.
*/
#define abs(x) __abs_choose_expr(x, long long, \
__abs_choose_expr(x, long, \
__abs_choose_expr(x, int, \
__abs_choose_expr(x, short, \
__abs_choose_expr(x, char, \
__builtin_choose_expr( \
__builtin_types_compatible_p(typeof(x), char), \
(char)({ signed char __x = (x); __x<0?-__x:__x; }), \
((void)0)))))))
#define __abs_choose_expr(x, type, other) __builtin_choose_expr( \
__builtin_types_compatible_p(typeof(x), signed type) || \
__builtin_types_compatible_p(typeof(x), unsigned type), \
({ signed type __x = (x); __x < 0 ? -__x : __x; }), other)
#define netdev_warn(dev, format, ...) printf("%s" format, "netdev_warn: ", ##__VA_ARGS__);
#define netdev_err(dev, format, ...) printf("%s" format, "netdev_err: ", ##__VA_ARGS__);
#endif /* __CTU_CAN_FD_LINUX_DEFS__ */
......@@ -81,7 +81,7 @@ enum ctu_can_fd_regs {
CTU_CAN_FD_TX_COUNTER = 0x80,
CTU_CAN_FD_DEBUG_REGISTER = 0x84,
CTU_CAN_FD_YOLO_REG = 0x88,
CTU_CAN_FD_TXTB1_DATA_1 = 0x100,
CTU_CAN_FD_TXTB1_DATA_1 = 0x100, // TODO: really count from 1?
CTU_CAN_FD_TXTB1_DATA_2 = 0x104,
CTU_CAN_FD_TXTB1_DATA_20 = 0x14c,
CTU_CAN_FD_TXTB2_DATA_1 = 0x200,
......
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>
//#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[])
{
uint32_t addr_base;
unsigned ifc = 0;
bool do_transmit = false;
//bool do_showhelp = false;
int c;
char *e;
const char *progname = argv[0];
while ((c = getopt(argc, argv, "i:th")) != -1) {
switch (c) {
case 'i':
ifc = strtoul(optarg, &e, 0);
if (*e != '\0')
err(1, "-i expects a number");
break;
case 't': do_transmit = true; break;
case 'h':
printf("Usage: %s [-i ifc] [-t]\n\n"
" -t: Transmit\n",