-3

Just to avoid ab-interpretation, my question in short:

I want to read and write to Arduino port D via llvm C++ api.

write

DDRD = 0xFF;
PORTD = 12;

read:

DDRD = 0x00;
int x = PORTD;

Read more:

I like to know how to read/write Arduino ports via llvm. I am not after a specific AVR micro-controller just need the general knowledge.

My effort was godbolt which does not work. I am not sure if what I need is supported ther.

https://godbolt.org/z/Wb5aGs

I tried it locally,

main.cpp

void foo(){
    DDRD = 0xFF;
    PORTD = 12;
}
clang++ -c -O0 --target=avr -mmcu=atmega328p -DF_CPU=16000000 -emit-llvm main.cpp

clang: error: unknown argument: '-mmcu=atmega328p'


How can I implement this via llvm::AllocaInst ? or reading this port via llvm::LoadInst?

I am mainly after C++ api solution. The IR llvm is not ideal for me but it will help me eventually finding the answer to the main question.

ar2015
  • 5,558
  • 8
  • 53
  • 110
  • 1
    No, because it’s unlikely anyone can know what you want to achieve. You’re at the same time talking about *building* something using LLVM, and also about using the LLVM API or IR. Your statement “I want to know how to read Arduino ports via LLVM” is meaningless. You want to know how to do it from a C++ program you compile, or when you are generating IR yourself, or what. The question is incomprehensible. Please speak your mind clearly. Your first problem is that there is no AVR target in your build of llvm, or you are not naming it properly. Make sure you understand what targets are available. – Kuba hasn't forgotten Monica Dec 12 '20 at 03:13
  • @UnslanderMonica, As I have stated, I am after LLVM api. But if no one knows that, I am satisfied at least by IR. – ar2015 Dec 12 '20 at 03:17
  • 2
    You have stated nothing that is self-coherent in the question. Why do you think you need an LLVM API? What do you mean by that? It looks like you’re mixing things up. LLVM API is not for use from inside the application being compiled with LLVM. It is for use in code generators, code analyzers, LLVM passes/backends, etc. If you’re asking how to generate target-specific I/O port output instructions from C/C++ code being compiled with LLVM, you have to see documentation of the target for the intrinsics that would be used for that. – Kuba hasn't forgotten Monica Dec 12 '20 at 03:43

1 Answers1

1

llvm C++ api

Nope. Those words don’t mean what you seem to imply. The “llvm C++ API” is not available from inside a program that you compile for, say, an Arduino. See below the line at the end of this answer for details. If you actually had LLVM C++ API in mind, you wouldn’t be asking this question since register access is not much different from any other constant pointer dereferencing, and requires no special treatment.

First note that Arduino is a concept, not any particular CPU. You need to refer at least to the particular Arduino model, eg. UNO, but it’s better to speak of a particular architecture like AVR.

Assuming that that’s what you want: There are no “ports” on AVR, so you don’t need to do anything special. Just write to memory with a given fixed address - both C and C++ support it on all sensible platforms.

The peripheral and CPU control registers are memory-mapped. So suppose you have a byte-wide control register at address 0x1234. You might define it as:

#include <stdint.h>
...
#define CREG (*(volatile uint8_t*)0x1234)

Then, a statement CREG = 42; will write the value 42 to that register at address 0x1234. That’s all there’s to it. You’ll find register addresses in the documentation for the given MCU.

Of course this is only a first step, you’d probably want a higher level API than this. I find it practical to use references instead of such evil macros. They provide more compile-time type safety. In C++:

constexpr volatile uint8_t &CREG = *(volatile uint8_t)0x1234;
...
CREG = 42;

Read-only registers are also possible:

constexpr volatile const char &RXD0 = *(volatile const char)0x5678;

RXD0 = 3; // won’t compile
char c = RXD0; // will compile OK

Sometimes you need to do a multi-step operation to access a register, eg. set some address register first, and then load it indirectly via an access register. Accessor functions do a good job:

enum class AREG_ : uint8_t { QREG = ... };
constexpr volatile AREG_ &AREG = *(volatile AREG_*)0x3456;

inline void setQREG(uint8_t val) { AREG = AREG_::QREG; BREG = val; }

LLVM API is used by tools, that is by software that use LLVM to analyze code or generate code. You can’t use LLVM API from an arbitrary program you’re compiling using LLVM, just as you can’t use gcc API from within the Arduino IDE targeting AVR, which uses avrg++ to compile for the AVR targets.

LLVM IR is what the front-ends produce, and you can’t embed IR within C nor C++, so I don’t know why you asked about that either.

I still have no idea what you are trying to do. Can you explain in simple words? Do you just want to compile an arbitrary Arduino program using clang? Your question is especially confusing since the words you use have clear technical meaning but you use them in a way that taken together makes no sense.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Thank you very much for the answer. I am using llvm c++ api to develop a tool for compiling to for arduino target. My tool is in C++. – ar2015 Dec 12 '20 at 04:44
  • I think I may have some idea. If the address of the memory-mapped port is 1234, then generating a write of the value 56 to it involves [new StoreInst](https://llvm.org/doxygen/classllvm_1_1StoreInst.html#a88cd6e29ad5e2f2b5d74ece3dd27fa81)(ConstantInt::get(…, 56), [ConstantExpr::getintToPtr](https://llvm.org/doxygen/classllvm_1_1ConstantExpr.html#a02560cda155aaed54314383bed827d60)(ConstantInt::get(…, 1234), …), …); – arnt Dec 12 '20 at 13:44