Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
12 years ago

Avalon- Wishbone Wrapper OpenCores CAN Protocoll Controller

Hi, I know that this has been discussed in some old threats but I could not get some real help from those. I want to use the CAN Protocoll Controller in my NIOSII system designed with QSYS.

I took a look at the two bus specifications and came up with this wrapper:


library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity OC_Can is
    port (
        avs_s0_address       : in  std_logic_vector(7 downto 0)  := (others => '0'); --    s0.address
        avs_s0_read          : in  std_logic                     := '0';             --      .read
        avs_s0_readdata      : out std_logic_vector(31 downto 0);                    --      .readdata
        avs_s0_write         : in  std_logic                     := '0';             --      .write
        avs_s0_writedata     : in  std_logic_vector(31 downto 0) := (others => '0'); --      .writedata
        avs_s0_readdatavalid : out std_logic;                                        --      .readdatavalid
        avs_s0_waitrequest    : out std_logic     := '0';
        clk                  : in  std_logic                     := '0';             -- clock.clk
        reset                : in  std_logic                     := '0';             -- reset.reset
        avs_irq_n         : out std_logic;                                         --  irq0.irq
        
        clk_i        :     IN STD_LOGIC;
        rx_i        :     IN STD_LOGIC;
        tx_o        :     OUT STD_LOGIC;
        bus_off_on        :     OUT STD_LOGIC;
        irq_on        :     OUT STD_LOGIC;
        clkout_o        :     OUT STD_LOGIC
        
    );
end entity OC_Can;
architecture rtl of OC_Can is
COMPONENT can_top
    GENERIC ( Tp : INTEGER := 1 );
    PORT
    (
        wb_clk_i        :     IN STD_LOGIC;
        wb_rst_i        :     IN STD_LOGIC;
        wb_dat_i        :     IN STD_LOGIC_VECTOR(7 DOWNTO 0);
        wb_dat_o        :     OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
        wb_cyc_i        :     IN STD_LOGIC;
        wb_stb_i        :     IN STD_LOGIC;
        wb_we_i        :     IN STD_LOGIC;
        wb_adr_i        :     IN STD_LOGIC_VECTOR(7 DOWNTO 0);
        wb_ack_o        :     OUT STD_LOGIC;
        clk_i        :     IN STD_LOGIC;
        rx_i        :     IN STD_LOGIC;
        tx_o        :     OUT STD_LOGIC;
        bus_off_on        :     OUT STD_LOGIC;
        irq_on        :     OUT STD_LOGIC;
        clkout_o        :     OUT STD_LOGIC
    );
END COMPONENT;
signal wb_readdata : STD_LOGIC_VECTOR(7 downto 0);
signal wb_writedata : STD_LOGIC_VECTOR(7 downto 0);
signal wb_stb : STD_LOGIC;
signal wb_ack : STD_LOGIC;
signal wb_cyc : STD_LOGIC;
signal avs_irq : STD_LOGIC;
signal av_readdatavalid : STD_LOGIC;
begin
can_inst : can_top
PORT MAP
    (
        wb_clk_i        => clk,
        wb_rst_i        => reset,
        wb_dat_i        => wb_writedata,                    --write data
        wb_dat_o        => wb_readdata,                    --read data
        wb_cyc_i        => wb_cyc,                            --when asserted indicates that valid bus cycle is in progress
        wb_stb_i        => wb_stb,                            --when asserted indicates that the slave is selected, slave should not respond till stb_i is asserted
        wb_we_i        => avs_s0_write,                    --deasserted during read cycles, asserted during write cycles
        wb_adr_i        => avs_s0_address,
        wb_ack_o        => wb_ack,                            --when asserted indicates termination of normal bus cycle
        clk_i            => clk_i,
        rx_i            => rx_i,
        tx_o            => tx_o,
        bus_off_on    => bus_off_on,
        irq_on        => avs_irq,
        clkout_o        => clkout_o
    );
    wb_stb <= avs_s0_read OR avs_s0_write;
    
    wb_cyc <= avs_s0_read OR avs_s0_write;
    
    avs_s0_waitrequest <= not wb_ack;
    
    avs_s0_readdatavalid <= wb_ack;
    --extend 8bits coming from can core to full 32 bits for avalon
    avs_s0_readdata <= "000000000000000000000000" & wb_readdata;
    
    --map lowest 8bits coming from avalon to writedata of can core
    wb_writedata <= avs_s0_writedata(7 downto 0);
    --avs_s0_readdatavalid <= '0';
    avs_irq_n <= not avs_irq;;
end architecture rtl; -- of OC_Can

As I can not simulate it for mixed signal reasons, I tried to access the CAN Core with the following very basic programm from the NIOS:


# include <stdio.h># include <io.h># include "system.h"# include "sys/alt_stdio.h"
int main()
{
  unsigned int temp;
  printf("Hello from Nios II!\n");
  temp = IORD_32DIRECT(OC_CAN_0_BASE,4);
  printf("%u",temp);
  while(1);
  return 0;
}

What I'm experiencing is that the CPU is getting stuck at the IORD_32DIRECT access.

Can anyone see where the problem might be hidden? As dev board I'm using a de0-nano. Did anyone get this CAN Controller running on a cyclone IV device?

Thanks in advance!

10 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I used the OC CAN controller on CIII and this is my wrapper (Verilog).

    As you can see, I don't use readdatavalid signal.

    
    module CAN_av_wrapper(
         //Avalon common
        input                   av_clk,
        input                   av_reset,
        //Avalon control port
        input              av_address,
        input                   av_chipselect,
        input                   av_write,
        input                   av_read,
        input             av_writedata,
        output            av_readdata,
        input              av_byteenable,
        output                  av_waitrequest_n,
    	// CAN interface
    	input					CAN_clk,
    	input					CAN_reset,
    	input					CAN_rx,
    	output					CAN_tx,
    	output       			CAN_bus_off,
    	output       			CAN_irq,
    	output       			CAN_clkout
    	
    );
    wire wb_ack_o;
    assign av_waitrequest_n = wb_ack_o;
    wire wb_cs_i;
    assign wb_cs_i = (av_byteenable == 4'b0001);
    assign av_readdata = 24'hz;
    can_top wishbone_can_inst
    ( 
      .wb_clk_i(av_clk),
      .wb_rst_i(av_reset | CAN_reset),
      .wb_dat_i(av_writedata),
      .wb_dat_o(av_readdata),
      .wb_cyc_i(av_write | av_read),
      .wb_stb_i(av_chipselect & (av_write | av_read)),
      .wb_we_i(av_write & ~av_read),
      .wb_adr_i({2'b0, av_address}),
      .wb_ack_o(wb_ack_o),
      .clk_i(CAN_clk),
      .rx_i(CAN_rx),
      .tx_o(CAN_tx),
      .bus_off_on(CAN_bus_off),
      .irq_on(CAN_irq),
      .clkout_o(CAN_clkout)
    );
    endmodule
    
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Thanks a lot for the help! There's some communication going on now! You pointed out some major flaws in my wrapper. The chipselect could actually just be skipped, as it suggested in the avalon specification? But I don't understand the addressing

    .wb_adr_i({2'b0, av_address})

    My thoughts where: avalon fabric is addressing the slave on a 32bit wide words basis, while the CAN Controller has a set of byte registers where each address maps to one register. As the data is expanded to 32bits I would have expected a one to one mapping of the addresses. Probabaly you could give me a jumpstart here.

    The read/write accesses are not blocking the system anymore but I'm not sure if the result does make sense. If I use the following code to read/write registers

    
      printf("Goint to reset mode \n");
      IOWR_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,0,1);
      for(i = 4; i < 120; i += 4) {
            temp = IORD_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,i);
            printf("%u: R %u ",i/4,temp);
              IOWR_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,i,255);
              temp = IORD_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,i);
              printf("W+R255 %u ", temp);
              printf("\n");
      }
    
    it outputs the following:

    --- Quote Start ---

    Goint to reset mode

    0: R 1 W+R255 0

    1: R 0 W+R255 0

    2: R 0 W+R255 0

    3: R 0 W+R255 0

    4: R 0 W+R255 0

    5: R 0 W+R255 0

    6: R 0 W+R255 0

    7: R 0 W+R255 0

    8: R 4 W+R255 4

    9: R 4 W+R255 4

    10: R 4 W+R255 4

    11: R 4 W+R255 4

    12: R 0 W+R255 0

    13: R 0 W+R255 0

    14: R 0 W+R255 0

    15: R 0 W+R255 0

    16: R 255 W+R255 0

    17: R 0 W+R255 0

    18: R 0 W+R255 0

    19: R 0 W+R255 0

    20: R 0 W+R255 0

    21: R 0 W+R255 0

    22: R 0 W+R255 0

    23: R 0 W+R255 0

    24: R 255 W+R255 255

    25: R 255 W+R255 255

    26: R 255 W+R255 255

    27: R 255 W+R255 255

    28: R 255 W+R255 255

    29: R 255 W+R255 255

    --- Quote End ---

    The avalon clock is of course connected to the nios clock (100MHz), the external clock to a 10MHz clock, reset to the global reset and the other exports to pins of the IO header of the de0-nano - no CAN Transceiver yet. But the reading/writing of the registers should work. Something is going wrong somewhere...

    What were your experiences with the controller in general. Is it working reliably and thus usable?

    Thanks again!
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    It has been quite a long time since I worked with this CAN controller, so I don't remember everything exactly.

    Anyway, I try to recollect here some information.

    .wb_adr_i({2'b0, av_address[7:2]})

    As you correctly pointed out, this is because Avalon is 32bit wide but it uses byte addressing. In a perfect world I've used the CAN controller with byte accesses (i.e. IORD_8DIRECT,IOWR_8DIRECT). Unfortunately Avalon fabric generates 4 8bit reads even with IORD_8DIRECT, because it requires a 32bit complete access to be performed: this is generally invisible to the application, since only the requested byte is returned by IORD_8DIRECT, but it makes a mess with devices involving fifo register which automatically pop data at every read access, and this CAN controller is one of them.

    That's why I remapped CAN register in order to access them as if they were 32bit wide.

    Please note that you must feed register addresses with multiple of 4, and discard higher 24 bits:

    data = IORD(OC_CAN_VERILOG_WRAP_0_BASE, reg*4) & 0xFF;

    IOWR(OC_CAN_VERILOG_WRAP_0_BASE, reg*4, data);

    The CAN controller works. I had some problems with FIFOs because of what I said above, but then it worked fine.

    I suggest you use 24 or 40MHz for CAN clock; IIRC with 10MHz you can't achieve all standard CAN baudrates
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I'm new with the use of IP cores and this forum has been helpful to use the CAN controller from OpenCores. At the moment I've added the component to avalon bus, following the Wrapper, in a project with the DE0-nano development board, but I'm still working with the software interface.

    The documentation of the opencores project indicates that CAN controller description is compatible with the SJA1000 controller, but want to know if the registers address are identical to those of SJA1000 controller?, there is a problem when accessing registers? or just the need of addresses multipled by 4?

    thanks and I hope early reply.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Hi,

    for you and the people of the future here the wrapper I used:

    module OC_Can_av_wrapper(
         //Avalon common
        input                   av_clk,
        input                   av_reset,
        //Avalon control port
        input              av_address,
        input                   av_chipselect,
        input                   av_write,
        input                   av_read,
        input             av_writedata,
        output            av_readdata,
        input              av_byteenable,
        output                  av_waitrequest_n,
    	// CAN interface
    	input		CAN_clk,
    	input		CAN_reset,
    	input		CAN_rx,
    	output		CAN_tx,
    	output       	CAN_bus_off,
    	output       	CAN_irq,
    	output       	CAN_clkout
    );
    wire wb_ack_o;
    assign av_waitrequest_n = wb_ack_o;
    assign av_readdata = 24'hz;
    can_top wishbone_can_inst
    ( 
      .wb_clk_i(av_clk),
      .wb_rst_i(av_reset | CAN_reset),
      .wb_dat_i(av_writedata),
      .wb_dat_o(av_readdata),
      .wb_cyc_i(av_write | av_read),
      .wb_stb_i(av_chipselect & (av_write | av_read)),
      .wb_we_i(av_write & ~av_read),
      .wb_adr_i({av_address}),
      .wb_ack_o(wb_ack_o),
      .clk_i(CAN_clk),
      .rx_i(CAN_rx),
      .tx_o(CAN_tx),
      .bus_off_on(CAN_bus_off),
      .irq_on(CAN_irq),
      .clkout_o(CAN_clkout)
    );
    endmodule

    Using this wrapper, the addresses should be according to the data sheet.

    Here a little snippet I used to verify the correct behaviour:

    #include <stdio.h>
    # include <io.h>
    # include "system.h"
    # include "sys/alt_stdio.h"
    int main()
    {
      unsigned int temp;
      int i = 0;
      printf("Control Register \n");
      temp = IORD(OC_CAN_VERILOG_WRAP_0_BASE,0x0000);
      printf("%u : R %u \n",i,temp);
      printf("Goint to OP mode \n");
       IOWR_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,0,32);
       temp = IORD_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,0x0000);
       printf("%u : R %u \n",i,temp);
      /*printf("Goint to reset mode \n");
      IOWR_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,0,32);
      temp = IORD_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,0x0000);
      printf("%u : R %u \n",i,temp); */
      printf("Double check \n");
      temp = IORD(OC_CAN_VERILOG_WRAP_0_BASE,0x0000);
      printf("%u : R %u \n",i,temp);
      printf("Read Clock Divider Reg \n");
      i=31;
      temp = IORD(OC_CAN_VERILOG_WRAP_0_BASE,31);
      printf("%u : R %u \n",i,temp);
      printf("Set it to extended mode \n");
      IOWR(OC_CAN_VERILOG_WRAP_0_BASE,31,128);
      temp = IORD(OC_CAN_VERILOG_WRAP_0_BASE,31);
      printf("%u : R %u \n",i,temp);
      printf("And back \n");
      IOWR(OC_CAN_VERILOG_WRAP_0_BASE,31,0);
        temp = IORD(OC_CAN_VERILOG_WRAP_0_BASE,31);
        printf("%u : R %u \n",i,temp);
      for(i = 1; i < 32; i++) {
    	  IOWR_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,i*4,0);
    	  temp = IORD_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,i*4);
    	  printf("%u: R %u \n",i,temp);
      }
      printf("Goint to reset mode \n");
      IOWR_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,0,33);
      temp = IORD_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,0x0000);
      printf("%u : R %u \n",i,temp);
      printf("Double check \n");
      temp = IORD(OC_CAN_VERILOG_WRAP_0_BASE,0x0000);
      for(i = 1; i < 32; i++) {
      	  IOWR_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,i*4,0);
      	  temp = IORD_32DIRECT(OC_CAN_VERILOG_WRAP_0_BASE,i*4);
      	  printf("%u: R %u \n",i,temp);
        }
      printf("\nDONE");
      return 0;
    }

    I mixed a bit the use of IO** and IO**_32DIRECT. This shouldn't be a problem as long as you are aware of the difference (see this thread (http://www.alteraforum.com/forum/showthread.php?t=32282)).

    The activation of the reset mode is commented to test if the core can just be set to extended mode if in reset mode (see data sheet (http://www.nxp.com/documents/data_sheet/sja1000.pdf) at page 56).

    I hope this helps!
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Hi, I need help, I'm testing the CAN controller with a DE0-nano board and I used the wrapper and it works fine, I have made the software test and it responds, but when I try to send a message, the can controller generates errors (reflected in the status reg). I'm using a 25Mhz can clock and I don't know what else should I test, can you help me, I send the code used in software to test and send a message.

    main program code:

    
    printf("Start CAN operation:\n");
          unsigned int temp;
          i=0;
          read_status();
          reset_mode();
          read_status();
          op_mode();
          read_status();
          can_init(Basic_can , 0x00FF, 0x00FF, Fosc_14);
          read_status();
          can_tx= 0x0F;
          can_tx= 0x0E;
          can_tx= 0x0D;
          can_tx= 0x0C;
          can_tx= 0x0B;
          can_tx= 0x0A;
          can_tx= 0xF1;
          can_tx= 0xF7;
          reset_mode();
          read_regs_can();
          op_mode();
          read_regs_can();
          send_msg(can_tx, 0x0001, 0x08, 0);
          read_regs_can();
          read_status();
          while(1)
          {}
    

    in a header file (M_can.h) I define:# define Basic_can 0x00# define Peli_can 0x80# define Fosc_14 0x06# define Bus_Tx_libre 0x0C

    in a source file (M_can.c) I define the functions:

    # include"M_can.h"
    # include <stdio.h># include <string.h>
    void reset_mode()
    {
        unsigned char temp;
        printf("Reset mode\n");
        temp = IORD_32DIRECT(CAN_CONTROLLER_0_BASE,0x0000);      
        printf("%u : Reg %X h \n",0,temp);
        printf("%u : Reg %u decimal \n",0,temp);
        IOWR_32DIRECT(CAN_CONTROLLER_0_BASE,0, (temp | 0x01));   
        temp = IORD_32DIRECT(CAN_CONTROLLER_0_BASE,0x0000);
        printf("%u : Reg %X h \n",0,temp);
        printf("%u : Reg %u decimal \n",0,temp);
    }
    void op_mode()
    {
        unsigned char temp;
        printf("OP mode:\n");
        temp = IORD_32DIRECT(CAN_CONTROLLER_0_BASE,0x0000); 
        printf("%u : Reg %X h \n",0,temp);
        printf("%u : Reg %u decimal \n",0,temp);
        IOWR_32DIRECT(CAN_CONTROLLER_0_BASE,0, (0xFE & temp));   
        temp = IORD_32DIRECT(CAN_CONTROLLER_0_BASE,0x0000);
        printf("%u : Reg %X h \n",0,temp);
        printf("%u : Reg %u decimal \n",0,temp);
    }
    unsigned char read_status()
    {
        unsigned char status;
        printf("reading status \n");
        status = IORD(CAN_CONTROLLER_0_BASE,2);
        printf("Now %u : Reg %X h \n",2,status);
        printf("%u : Reg %u decimal \n",2,status);
        return(status);
    }
    void read_regs_can(void)
    {
        int i;
        unsigned char temp;
        for(i = 0; i < 32; i++)
              {
                  temp = IORD_32DIRECT(CAN_CONTROLLER_0_BASE,i*4);
                  printf("Reg %u : %X h        ",i,temp);
                  printf("Reg %u: %u decimal \n",i,temp);
              }
    }
    void send_msg(unsigned char *vector, unsigned int id, unsigned char long_bytes_msg,  unsigned char rtr)
    {
        unsigned char filtro_h, filtro_l, status;
        int i;
        printf("sending msg");
        status=0x00;
        filtro_h = (id >> 3);                     
        filtro_l = (id << 5);                     
        if (rtr ==1)
            filtro_l = filtro_l | 0x10;
        if(long_bytes_msg > 0x08)
            long_bytes_msg = 0x08;
        filtro_l = filtro_l | long_bytes_msg;  
        IOWR(CAN_CONTROLLER_0_BASE,10,filtro_h);  
        IOWR(CAN_CONTROLLER_0_BASE,11,filtro_l);   
        for(i=0 ; i<long_bytes_msg ; i++)
        {
            IOWR(CAN_CONTROLLER_0_BASE,(12+i),*vector);  
            vector = vector+1;
        }
        // sending request
        IOWR(CAN_CONTROLLER_0_BASE,1,0x01);
        while(status != Bus_Tx_free)
        {
            status = read_status();
            status = status & Bus_Tx_libre;
        }
        printf("Msg Sended \n");
    }
    void can_init(unsigned char mode, unsigned int masc, unsigned int filtro, unsigned char fout)
    {
        printf("init can \n");
        unsigned char temp;
        reset_mode();
        temp = modo | fout;   
        temp = temp | 0x60;
        IOWR(CAN_CONTROLLER_0_BASE,31,temp); 
        IOWR(CAN_CONTROLLER_0_BASE,4,filtro);
        config_timing_reg(1000000, SJW_1, SAM_1);
        config_output(normal_mode, push_pull);
        op_mode();
        printf("end init \n");
    }
    void config_timing_reg(float frec_CAN, unsigned char SJW, unsigned char muest)
    {
        float BRP;
        unsigned int BRP_int;
        unsigned char temp, temp2;
        BRP = (( (F_crystal * T_crystal) / (2.0) ) - 1.0);
        BRP_int = BRP;                                            // casting a int.
        temp = BRP_int;                                            // casting a char.
        temp = SJW | temp;
        IOWR(CAN_CONTROLLER_0_BASE,6,temp); 
        temp2 = muest | seg1 | seg2;
        IOWR(CAN_CONTROLLER_0_BASE,7,temp2);                 
    }
    void config_output(unsigned char mode, unsigned char output_pol)
    {
        unsigned char temp;
        temp= mode | output_pol;
        IOWR(CAN_CONTROLLER_0_BASE,8,temp);                   
    }
    

    when I test the main program, the CAN controller send bits in the Tx pin, but then the Bus_off pin changes and in the status reg I see the 0xD4h errors, I don't know if maybe the process of sending the message is right.

    thanks.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I don't have the time to carefully analyze your code now.

    Just a few question which could help to identify your problem:

    - do you connect to a CAN device on the other side or do you test the core without bus connection?

    - are timing registers correctly configured for the bus baudrate? IIRC 25MHz clock frequency is generally not used for CAN since you can't achieve all standard baudrates; if you can have it, use 24MHZ or 48MHz.

    - Do you see any waveform on bus lines when you call the send_msg the first time?
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Hi, acording to questions, fistly, I was testing the can core without an external can device, and when I read the status reg, after a transmission request, I find a 0xD4 wich means bus_off, error, Tx in progress and Tx buffer released. Then I use a MCP2551 can transceiver and a external can device, another MCP2551 and a PIC30F4013 to test it. with the external hardware, I send again a message and find in the status reg again a 0xD4, so I don't know what is happend. I have tested the status reg in every step of initialization but when I send the comand for transmition request the status changes from 0x0C (Tx complete and Tx buffer released) to 0xD4, in this process to changes I see that the can controller send a package in the Tx pin, but with an osciloscope it seem to be the error frame.

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    in this process to changes I see that the can controller send a package in the Tx pin, but with an osciloscope it seem to be the error frame.

    --- Quote End ---

    What do you mean with error frame? On tx pin you must see the frame you write in the tx buffer, namely can_tx[] values coded according to CAN protocol.

    An error frame would be possibly transmitted back from the can slave.

    A few stupid questions:

    Did you check with the oscilloscope if the baudrate you see on tx pin is correct?

    Is PIC30F4013 CAN interface set to the same baudrate?

    Did you try the rx as well? I mean transmit from the PIC30F and check if you receive anything on the DE2-nano