BCM Firmware Reverse-Engineering (HS-CAN Kommunikation)

  • In diesem Bereich versuche ich mich zu den Funktionen rund um die HS-CAN Anbindung des BCM vorzuarbeiten. Die gesamte Programmierung des BCM geschieht ausschließlich über den HS-CAN. Der MS-CAN sowie MM-CAN dienen nur der Kommunikation mit den Modulen. Daher ist der HS-CAN für mich erstmal der interessantere.

    Welcher CAN-Controller bedient den HS-CAN?

    "Wie immer" starte ich mit der Hardware. Aus dem Basis-Untersuchen ist ja schon bekannt das ein R32C auf dem BCM arbeitet. Dieser hat laut Datenblatt 3 CAN-Module (Controller)


    Los geht es am Stecker. Der HS-CAN Bus liegt am "blauen" BCM-Stecker (Armaturenbrett-Kabelbaum) C3 and den Pins 6 und 7 an:


    Auf der Platine des BCM wurde ordentlich mit Harz gekleckert. Das macht die Identifizierung der Bauteilbezeichnungen mitunter sehr schwer, weil man diesen erstmal vorsichtig runterkratzen muss. Ein CAN-Bus benötigt immer einen Transceiver (Pegelwandler). Hiervon finden sich gleich mehrere auf der Platine. Der vom HS-CAN ist ein TJA1042 (siehe Position (15) auf BCM Firmware Reverse-Engineering). Hier enden direkt die beiden Pins von der Buchse.


    Auf der anderen Seite des TJA1042 geht es dann zu den Pins 1 und 2 vom R32C:


    Die Pins von Microcontrollern wie dem R32C sind häufig nicht auf eine Funktion festgelegt, sondern abhängig von der Programmierung des Chips. Das Datenblatt zeigt dies durch mehrere Bezeichnungen mit "/" getrennt.


    Ein physikalischer Pin am Gehäuse des Chips kann durch Programmierung mit verschiedenen Modulen innerhalb des Chips verbunden werden, wie hier an dem Schaubild zu erkennen:


    Somit kann z.B. der "Pin 1" folgende Funktionen haben:

    • "P9_6" => GPIO Port, ein digitaler IO-Port Nummer #6 in der Portgruppe #9
    • "ANEX1" => Analoger, externer Watchdog #1
    • "TXD4" => Sendeleitung des seriellen UART #4
    • "CAN2OUT" => Sendeleitung des CAN-Controllers #2
    • "CAN1OUT" => Sendeleitung des CAN-Controllers #1

    Die Tatsache das der TXD des TJA1042 mit dem Pin 1 des R32C verbunden ist und der TJA1042 keinen Chip-Enable hat, lässt darauf schließen das es hier nur um die beiden CAN-Controller gehen kann. Welcher Controller nun am Ende die Arbeit macht, werden wir erst in der Initialisierungsroutine erkennen können.


    Klar ist jedoch das es im Code Zugriffe auf die Konfiguration von Port P9_5 und P9_6 geben muss. Um diese zu finden und zuzuordnen muss man die Funktionsweise der CAN Controller etwas besser verstehen. Hilfe hierzu gibt das "R32C Users Manual" R32C-152 Users Manual.pdf im Abschnitt "CAN Controller", als auch die "R32C CAN Applicaiton Note" R32C CAN Application Note.pdf.


    Im Schaubild eines CAN-Controllers erkannt man die Funktionsblöcke:


    An (1) kommen die physikalischen Signale vom CAN-Transceiver zum Controller (Der Zusatz "/CANxWU" bedeutet das dieser Pin auch zum aufwecken der MCU aus einem Schlafmodus genutzt werden kann. Auf Deutsch: Kommt über den CAN-Bus eine Botschaft, kann diese das BCM aus dem Schlafmodus holen). Zur Takterzeugung wird an (8) der CPU-Takt (8 MHz) verwendet um die Baudrate des CAN-Bus (bei HS-CAN 500 MBit/s) einzustellen.

    Hat der Protocoll Controller (2) z.B. ein eingehendes Datenpaket erkannt, leitet er es an den Filter und Priorisierer (3) weiter, welcher es ggf. in eine Mailbox (4) verschiebt. Die mit (5) gekennzeichneten SFRs sind zur Programmierung der Komponenten gedacht und für uns die interessanten.

    Der Interrupt Generator (6) erzeugt je nach Betriebszustand verschiedene Ereignisse welche dann von der Software mittels Interrupt-Service-Routinen (ISRs) behandelt werden, so z.B. der Empfang einer Botschaft und die Verarbeitung der Daten darin.


    Die Programmierung des Controllers (2) entscheidet also welche CAN I/O pins genutzt werden. Das schaue ich mir genauer an. Irgenwo muss es einen Zusammenhang zwischen den Pins 5+6 von Gruppe 9 und einem CAN-Controller geben. Hierzu finde ich das "Port P9_i Function Select Register". Das "i" wird dann mit "5" oder "6" substituiert. Es gibt also für jeden einzelnen Port ein eigenes 8-Bit Register im SFR2:



    Die unteren 3 Bits geben die Konfiguration des Ports an, also bestimmen wohin dieser physikalische Pin intern verbunden wird. Die SFR2 Speicheradresse für Port P9_6 (CAN Ausgangsleitung) ist somit 0x400ed. Wird dort in die Bits 0-2 der Wert 0b101 geschrieben, ist er mit dem Controller CAN2 verbunden, beim Wert 0b110 mit dem von CAN1.


    Mittels einer Rückwärtssuche auf den Schreibzugriff auf diese Adresse finde ich im Code:

    Code
    CODE:FFFA1EEE                 MOV.B   #101b, p9_6s

    Auch für nicht-Assembler geschädigte gut zu erkennen das der Wert 0b00000101 auf die Adresse geschrieben wird. Der Disassembler hat hier bereits die Adresse 0x400ed durch den Namen "p9_6s" ersetzt, weil er den Registeraufbau der R32C kennt. Der Wert verrät und nun das der Pin P9_6 mit der Sendeleitung von Controller CAN2 verbunden ist.

    Somit ist klar das der HS-CAN vom Controller CAN2 bedient wird!

    Der gesamte Code um diese Stelle herum enthält die Programmierung des Controllers u.a. mit der Baudrate von 500 MBit/s, sowie der Einstellung zu Interrupts und Mailboxen. Jeder Kanal verfügt über bis zu 32 solcher "Mailboxen" in die eingehende oder zu Sendung ausgehende CAN-Botschaften zwischengespeichert werden. Damit sich die Software nicht mit jedem Datagramm auf dem Bus beschäftigen muss gibt es zusätzliche "Acceptance Filter" die nur solche Botschaften in die Mailboxen einleiten, welche auch zum Zuständigkeitsbereich des BCM gehören.


    Ich habe die Routine mit Kommentaren versehen. Wen das interessiert kann es ja mal durcharbeiten und mit dem Handbuch vergleichen:

    Aus dieser geht u.a. noch hervor das für den Empfang von CAN-Botschaften die Mailboxen MB0 und MB1 im "Normal Mode" (also kein FIFO) verwendet werden. Diese sind jeweils mit unterschiedlichen Acceptance-Filtern ausgestattet. Auch die Interrupts werden hier aktiviert. Dazu mehr im folgenden Beitrag.


    Die Filterungseinstellungen sind etwas komplex, ich versuche es so einfach wie möglich darzustellen.

    "Acceptance Filter" wirken auf die ID der CAN-Botschaft und erlauben es das nur ein bestimmter Bereich von IDs zugelassen wird. Dabei können für die insgesamt 32 Mailboxen pro CAN-Controller unterschiedliche Filter hinterlegt werden. Diese sind in Gruppen eingeteilt, ein Filter bedient dabei jeweils 4 aufeinanderfolgende Mailboxen. Für die MB0 und MB1 des CAN2, welche oben konfiguriert wurden, gilt dabei nur der Filter C2MKR0. Dieser Filter wird über die SFR2 Speicheradressen 0x47600..0x47603 eingestellt:



    Der dort hinterlegte 32-Bit Wert beschreibt mit 0 und 1 ob das jeweilige Bit mit der eingehenden CAN-ID verglichen wird oder nicht. Einfach ausgedrückt: wäre alles 0, dann würde der Filter jede CAN-ID akzeptieren und je mehr 1en man einbringt umso weniger IDs würden erkannt. EID/SID beschreibt die ID-Längen, wobei wir im Mondeo ausschließlich mit Standard-IDs (SID) arbeiten, die nur 11 Bit lang sind. Somit sind nur die Bits 18-28 ausschlaggebend.


    Interessanterweise finde ich bislang im gesamten Code keinen Zugriff auf diese Register. Das bedeutet das keine Acceptance Filter für HS-CAN eingestellt werden. Da der Reset-Value "undefined" ist, macht mir das etwas zu denken, aber mal schaun.


    Gefunden habe ich jedenfalls das die hscan_init Routine folgendes einstellt:

    • mb0 => Empfängt CAN-Botschaften mit der ID 0x7DF (UDS Broadcast Adresse)
    • mb1 => Empfängt CAN-Botschaften mit der ID 0x726 (BCM UDS Adresse für Anfragen)
    • mb2 => Sendet CAN-Botschaften mit der ID 0x72E (BCM UDS Adresse für Antworten)

    "Lernen ist Erfahrung. Alles andere ist einfach nur Information."

    Albert Einstein

  • Als nächstes gehe ich auf die Suche nach den Grundfunktionen die beim Empfang von Daten vom CAN-Bus aufgerufen werden. Diese sollten sich über die Interrupts leicht finden lassen. Aus der o.g. Analyse ist ja bekannt das beim Eingang an CAN2 ein entsprechender Interrupt ausgelöst wird. Laut Handbuch gibt es eine ganze Reihe von Triggern für einen Interrupt:

    • CAN wakeup interrupt

    • CAN reception complete interrupt

    • CAN transmission complete interrupt

    • CAN receive FIFO interrupt

    • CAN transmit FIFO interrupt

    • CAN error interrupt (Bus error, Error-warning, Error-passive, Bus-off entry, Bus-off recovery, Receive overrun, Overload frame transmission, Bus lock)


    Interessant wäre der "CAN reception complete interrupt" der ausgelöst wird, sobald ein gültiges CAN Frame gelesen und in eine Mailbox gepackt wurde:


    Es gibt zwei Arten von Interrupts, die direkten welche in einer fixen Tabelle untergebracht sind:


    Hier findet sich jedoch nichts bezüglich CAN.

    Es gibt noch eine "Relocatable Vector Table" welche durch Zuweisung einer Speicheradresse an INTB insegamt 256 Sprungvektoren aufnehmen kann (4 Byte x 256 = 1024 Byte). Im Code findet sich etwas das diese Basisadresse zuordnet:

    Code
    CODE:FFF40A15                 LDC     #0FFF4007Ch, INTB

    An der genannten Speicheradresse finden sich die Sprungadressen (LWORD). Im Handbuch sind diese in einer Tabelle aufgeschlüsselt:


    Die Adresse der ISR ist somit also an Offset-Position 0x19C zu finden, also 0xFFF4_007C + 0x19C = 0xFFF4_0218

    Code
    CODE:FFF40218 c2receiveInterrupt .LWORD #0FFF83AAA ; c2receiveInterruptHandler

    An dieser Adresse steht also der Code der nach Eingang einer CAN-Botschaft (egal welche Mailbox) die Datenbehandlung durchführt:

    Ab hier wird es etwas knifflig, da nun die Businesslogic mitspielt. Irgendwo in der Folge muss der Code jedoch überprüfen von welcher Mailbox der INTB ausgelöst wurde, also neue Daten angekommen sind. Laut Handbuch wird dazu das "NEWDATA" Bit im C2MCTL0 oder C2MCTL1 Register auf "1" gesetzt:


    Für jede Mailbox gibt es eine SFR-Adresse auf ein solche 1 Byte Register:

    Für die Mailbox 0 von CAN2 ist es 0x47720 und für Mailbox 1 0x47721.


    Der Code nutzt diese Adresse an folgenden Stellen:

    mb0:

    mb1:

    Interessant sind die Lesezugriffe, denn erstmal muss ja festgestellt werden ob etwas neues vorliegt. Hierfür kommen z.B. die BTST #0 Befehle in Frage, denn sie testen genau auf dieses Bit.

    "Lernen ist Erfahrung. Alles andere ist einfach nur Information."

    Albert Einstein

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!