Twincat 3: Giving names to array bits - twincat

Beginner in the whole PLC stuff, so corrections are welcome.
I am trying to tidy up my project and current situation is thus:
I receive 16 byte arrays from modbus. These act as buttons, lights, conveyors what have you in Factory IO.
GAB_FactoryIO_Inputs AT %I* : ARRAY [0..15] OF BYTE;
GAB_FactoryIO_Outputs AT %Q* : ARRAY [0..15] OF BYTE;
So instead of referring to "Start button" with its bit "IO.GAB_FactoryIO_Inputs[0].0" I made a clumsy conversion POU and GVL to go through each bit and give them a new name. So it currently it looks like 200 lines of this:
IO.iSensor10_Capa := IO.GAB_FactoryIO_Inputs[7].3;
IO.iSensor9_Capa := IO.GAB_FactoryIO_Inputs[7].4;
IO.iPositioner_Limit := IO.GAB_FactoryIO_Inputs[7].5;
IO.iPositioner_Clamped := IO.GAB_FactoryIO_Inputs[7].6;
IO.iPick2_Detected := IO.GAB_FactoryIO_Inputs[7].7;
IO.iPick2_MovX := IO.GAB_FactoryIO_Inputs[8].0;
IO.iPick2_MovZ := IO.GAB_FactoryIO_Inputs[8].1;
IO.iPick2_Rot := IO.GAB_FactoryIO_Inputs[8].2;
IO.iPick2_GripRot := IO.GAB_FactoryIO_Inputs[8].3;
And this
iPositioner_Limit : BOOL;
iPositioner_Clamped : BOOL;
iPick2_Detected : BOOL;
iPick2_MovX : BOOL;
iPick2_MovZ : BOOL;
iPick2_Rot : BOOL;
iPick2_GripRot : BOOL;
It all works as it should, but I cant help to feel its amateurish, unwieldy and slows things down.
I've read about structures, enumeration and alias, and thought structures would be my savior by handily arranging them inside "cabinets", like so:
stCNC.Button1
stCNC.Button3
stCNC.Sensor1
And hidden inside structures would be the conversion between stCNC Sensor1 = IO.GAB_FactoryIO_Inputs[9].4;
But for some reason that doesnt work at all.
I'm most likely going at it plain wrong angle, but have no idea what to look for next.
EDIT work in progress thus far. Seems like I got the hang of the basics.
#kolyur had simple enough example to follow, so I started from there, and progressed towards #Steve and #YAVA examples:
//sending inputs to GVL FactoryIO_Inputs AT %I* : ARRAY [0..15] OF BYTE;
fbMBReadInputs(pDestAddr := ADR(IO.FactoryIO_Inputs),
//in GVL IO
FactoryIO_Inputs AT %I* : U_UNION2;
//inside S_LIGHTS:
TYPE S_LIGHTS :
STRUCT
LIGHT0 : BIT;
LIGHT1 : BIT;
LIGHT2 : BIT;
LIGHT3 : BIT;
LIGHT4 : BIT;
LIGHT5 : BIT;
LIGHT6 : BIT;
LIGHT7 : BIT;
END_STRUCT
END_TYPE
//inside U_UNION1
TYPE U_UNION1 :
UNION
nArray : ARRAY[0..15] OF BYTE;
sName : S_NAME;
//Then instantiating in POU
VAR
sLights : S_LIGHTS;
---
sLights.LIGHT1 := TRUE;

You can try to use a UNION.
It is basically an "overlay" variable you can place over another variable.
TYPE Test :
UNION
nARRAY : ARRAY[0..15] OF BYTE;
sHumanReadable : sStruct;
END_UNION
END_TYPE
UNION on Infosys

A structure could help if you make use of the BIT datatype. BITs address individual bits (unlike BOOLs which require a whole byte) but they can only be used within structures.
TYPE Test
STRUCT
button1 : BIT;
button2 : BIT;
button3 : BIT;
button4 : BIT;
sensor1 : BIT;
sensor2 : BIT;
sensor3 : BIT;
sensor4 : BIT;
END_STRUCT
END_TYPE
This structure occupies one byte and you could make an array of them to potentially use in your Modbus routine instead of the byte arrays.

Instead of first linking each IO to a global variable list and then link them to a function block, you could link the symbols directly to an instance of a function block.
For example you make the following function block and put the hardware inputs in the VAR section or you could put them in VAR_INPUT. You could also collect the inputs into a struct as the others suggested and use this struct in the function block.
FUNCTION_BLOCK Picker
VAR
MoveX AT %I : BOOL;
MoveY AT %I : BOOL;
Rotate AT %I : BOOL;
GripRotate AT %I : BOOL;
END_VAR
Then in your program you make an instance of the picker
PROGRAM MAIN
VAR
picker1 : Picker;
END_VAR
And then from you system configuration you can link each terminal input directly to the VAR's inside picker1.

Related

How can i declare or set an auto value changeable boolean variable in twincat 3

I am new to TwinCAT programming. I want to set 4 boolean variables in TwinnCAT 3 and they will change value automatically after 150 milliseconds using structured text. How can I do that?
If you want to change values just for once after execute the timer, basicly you can use like this;
PROGRAM MAIN
VAR
bExecute : BOOL;
bVar1 : BOOL;
bVar2 : BOOL;
bVar3 : BOOL;
bVar4 : BOOL;
Timer1 : TON;
END_VAR
Define variables as above,
write your code as below;
Timer1(IN:= bExecute, PT:=T#150MS);
IF Timer1.Q THEN
bVar1 := NOT bVar1;
bVar2 := NOT bVar2;
bVar3 := NOT bVar3;
bVar4 := NOT bVar4;
bExecute := FALSE; // After executing, if you want to make false the bExecute variable and make Timer.IN false.
END_IF
For more information about Timers please visit here

How to make inputs of a function block method optional?

When calling a method of a function block, is it possible to make certain input variables optional? If I call fbA.methA() without assignments for all input variables, TwinCAT throws an error: "Function methA requires exactly 'x' inputs." There are times when some inputs are unnecessary or irrelevant, but so far I've had to assign dummy values to those inputs to get the code to compile.
I don't think that that is possible. You could make extra methods which all call a base method.
For example:
FUNCTION_BLOCK Multiplier
METHOD Multiply : REAL
VAR_INPUT
number1 : REAL;
number2 : REAL;
END_VAR
METHOD MultiplyByTwo : REAL
VAR_INPUT
number : REAL;
END_VAR
MultiplyByTwo := Multiply(2, number);
That way you also reduce the number of inputs of your method, thereby making it easier to test and use.
You also could screen the parameters as they are passed in (still requires parameters but they have no meaning aka always pass "0").
FUNCTION_BLOCK CAT
METHOD DECIBELS: REAL
VAR_INPUT
MEOW, PURR: BOOL;
END_VAR
// body
DECIBELS := 0.0;
IF MEOW <> 0
DECIBELS := DECIBELS + 10.0;
END_IF;
IF PURR <> 0
DECIBELS := DECIBELS + 5.0;
END_IF;
END_METHOD
END_FUNCTION_BLOCK
you can invoke this like:
PROGRAM MAIN
VAR
C: CAT;
RESULT: ARRAY [1..4] OF REAL;
END_VAR
// body
RESULT[1] := C.DECIBELS(TRUE, TRUE); // will return 15.0
RESULT[2] := C.DECIBELS(TRUE, 0); // will return 10.0
RESULT[3] := C.DECIBELS(0, TRUE); // will return 5.0
RESULT[4] := C.DECIBELS(0, 0); // will return 0.0
END_PROGRAM
Hope this helps

Is it possible to give a default to an output port, to allow sizing in the event the port is not required?

Lets say I have an entity that looks like this:
constant NULL_PORT : std_logic_vector(1 to 0);
entity some_verification_ip is
port (
write_port : in std_logic_vector := NULL_PORT;
read_port : out std_logic_vector -- Sized on connection
);
In one test, a user's DUT only does reads, and so only connects the read port, and the write port gets sized from the default assignment to the NULL_PORT constant.
In another test, a user's DUT only does writes, and only connects the write port, but you get an elaboration error because the read_port is not sized. So the user then needs a dummy_signal in the testbench (that isnt used) just to avoid this error.
You cannot do a := assignment to the output, as this is an initial value, and you get an elaboration error when the port is connected as the NULL_PORT width missmatches the user's port connection.
Is the dummy signal in the testbench the only way to get a size? Is this changing in VHDL 2018?
NOTE: In reality, my Verification IP is an AXI4 memory model, and in the design different parts handle reads and writes, so the various channels are not needed for all tests, but are needed for larger scale tests that cover reads and writes.
As #paebbles writes in the comments you can do such a thing when you add one or two generics to the module.
entity some_verification_ip is
generic (
in_width : positive; -- could use a := <default width>
out_width : positive -- could use a := <default width>
);
port (
write_port : in std_logic_vector(in_width-1 downto 0);
read_port : out std_logic_vector(out_width-1 downto 0)
);
And when i stantiating you can use
.
.
generic map(
in_witdth => inputsignal'length,
out_width => outputsignal'length
)
port map (
.
.
If the in- and output widths are the same one generic can be used.
Initialising can then be done in a function.
I think this should work, but maybe i forgot something. The example should be enough to get yoh started though.
function null_port(width : positive) return std_logic_vector is
ret : std_logic_vector(width-1 downto 0);
begin
ret := (others => '0'); --use loops or other more elaborate stuff to construct the value you need
return ret;
end function

Maintaining fixed memory addresses for record members in Ada

I installed the GNAT-GPS and the AVR-ELF 3 days ago to play with. I got a blinky going and thought I might play around some more. I have no non-VHDL Ada experience.
Here's the scenario I have working in C:
I have it set up so that using a GPIO typedef, I can refer to all the information necessary to set up an GPIO pin (i.e. pin number, pin reg address, dd reg address and port reg address). Then I do the same for, say LED0, so that logically I can connect LED0 to GPIO15, which is itself connected to PB1 of the AVR microcontroller.
I try to do the same in Ada. I feel like I might be writing C in Ada; feel free to let me know afterwards if there's a better way to do this in Ada.
I set up the AVR registers for a particular pin to connect to its short name reference:
-- PB1
PB1_Port_reg : Unsigned_8;
PB1_Dd_reg : Unsigned_8;
PB1_Pin_reg : Unsigned_8;
for PB1_Port_reg'Address use AVR.Atmega328p.PORTB'Address;
for PB1_Dd_reg'Address use AVR.Atmega328p.DDRB'Address;
for PB1_Pin_reg'Address use AVR.Atmega328p.PINB'Address;
PB1_Pin : constant := 1;
Then I setup its short name reference to connect to its package pin number:
-- ATmega328p DIP28 Pin15 is PB1
Pin15_Port_reg : Unsigned_8;
Pin15_Dd_reg : Unsigned_8;
Pin15_Pin_reg : Unsigned_8;
for Pin15_Port_reg'Address use PB1_Port_reg'Address;
for Pin15_Dd_reg'Address use PB1_Dd_reg'Address;
for Pin15_Pin_reg'Address use PB1_Pin_reg'Address;
Pin15_Pin : constant := PB1_Pin;
Next I define a record to hold all the parameters for the pin together:
type gpio_t is record
pin : Unsigned_8;
pin_reg : Unsigned_8;
dd_reg : Unsigned_8;
port_reg : Unsigned_8;
end record;
This is to allow me to write the following function:
procedure gpio_map (gpio_t_dest : in out gpio_t; gpio_t_pin, gpio_t_pin_reg, gpio_t_dd_reg, gpio_t_port_reg : in Unsigned_8) is
begin
gpio_t_dest.pin := gpio_t_pin;
gpio_t_dest.pin_reg := gpio_t_pin_reg;
gpio_t_dest.dd_reg := gpio_t_dd_reg;
gpio_t_dest.port_reg := gpio_t_port_reg;
end gpio_map;
In the future, I'll be looking to have it as:
procedure gpio_map_future (gpio_t_dest : in out gpio_t; gpio_t_src : in gpio_t) is
begin
gpio_t_dest.pin := gpio_t_src.pin;
gpio_t_dest.pin_reg := gpio_t_src.pin_reg;
gpio_t_dest.dd_reg := gpio_t_src.dd_reg;
gpio_t_dest.port_reg := gpio_t_src.port_reg;
end gpio_map;
This gpio_map function is used to connect a package pin gpio_t to a package pin number:
gpio_map(gpio15, Pin15_pin, Pin15_pin_reg, Pin15_dd_reg, Pin15_port_reg);
I find that the LED is correctly initialized if I use this function:
core_reg_write(Pin15_dd_reg, Shift_Left(1,Integer(Pin15_pin))); -- works
But is not correctly initialized if I do:
core_reg_write(gpio15.dd_reg, Shift_Left(1,Integer(gpio15.pin))); -- does not work
This, however, works:
core_reg_write(Pin15_dd_reg, Shift_Left(1,Integer(gpio15.pin))); -- works
It is clear to me that I have
Pin15_pin = 1 # address (don't care - a variable)
Pin15_pin_reg = (don't care) # address 0x23
Pin15_dd_reg = (0b00000000) # address 0x24
Pin15_port_reg = (don't care) # address 0x25
And that
gpio15.pin = 1 # address (don't care, but not same as Pin15_pin address)
gpio15.pin_reg = (don't care) # address IS NOT 0x23
gpio15.dd_reg = (don't care) # address IS NOT 0x24
gpio15.port_reg = (don't care) # address IS NOT 0x25
How do I maintain fixed memory addresses for record members, i.e., get
gpio15.pin_reg = (don't care) # address 0x23
gpio15.dd_reg = (don't care) # address 0x24
gpio15.port_reg = (don't care) # address 0x25
And even better if I can also get
gpio15.pin = 1 # address (same as Pin15_pin address)
Sorry for the long question; hoping it helped make it clear.
You can't really get what you want via assignment of the two types. All that does is copy the current values, not the register addresses. Here is an option:
Create a type similar to your gpio_t type but make it exactly match the register map for your micro. That means you won't be storing the pin number in it and you need to include all the surrounding registers. Here is an example I found from another file for a different micro, but hopefully serves as an example
type Register_Layout is limited record
DIR : Unsigned_32;
DIRCLR : Unsigned_32;
DIRSET : Unsigned_32;
DIRTGL : Unsigned_32;
OUTVAL : Unsigned_32;
OUTCLR : Unsigned_32;
OUTSET : Unsigned_32;
OUTTGL : Unsigned_32;
INPUT : Unsigned_32;
CTRL : Unsigned_32;
WRCONFIG : Unsigned_32;
EVCTRL : Unsigned_32;
end record
with
Pack,
Volatile,
Size => 12*32;
The record type should be limited so that you ensure it is passed by reference and not by copy.
Note: You can also use a representation clause to provide the byte and bit layout of the structure instead. It will depend on the compiler that you use.
Once you have your micro's registers laid out to match the datasheet, you then create a variable and map that to the address you want, just like you did with the individual variables
Register_B : Register_Layout with
Address => System'To_Address(Some_Address),
Volatile => True,
Import => True;
This will map the entire record variable to that address.
After that, you need to modify your function calls to take the whole record as a parameter instead of the just the register. As an example:
Core_Reg_Write_DIR(Register_B, Shift_Left(1,Integer(PB1_Pin)));
If you need to have things be more fancy and have the right registers and mask value selected via pin, then you either need to use
CASE statements
Arrays of access types/addresses (using the pin type as the index).
A way to calculate the register address and mask from the pin and use that on a locally declared variable's address attribute inside a function call using a pin as a parameter.
You can't really have individual record components addressed differently (this is true in C and C++ as well).
Ok, after looking at your example, I came up with a similar solution in Ada. That said, I don't really care for how exposed access types are here. I'll leave my previous answer since I feel using records directly is a better method overall, but to specifically answer your question, here is an example I tested out in GNAT GPL 2017 using a handmade runtime (for another chip, but it was enough to verify compilation). Trying to compile it in a non embedded version of GNAT met with compiler crashes (I am assuming because the addresses were bad for windows). Hopefully this gives an example that better fits your personal requirements
registers.ads
with Interfaces;
-- Basic Register type and functionality
package Registers with Pure is
type Register is limited private;
type Register_Access is access all Register with Storage_Size => 0;
procedure Core_Reg_Write
(Target : not null Register_Access;
Value : Interfaces.Unsigned_8)
with Inline;
function Core_Reg_Read
(Source : not null Register_Access)
return Interfaces.Unsigned_8
with Inline;
private
type Register is limited record
Value : Interfaces.Unsigned_8;
end record
with Volatile, Size => 8;
end Registers;
registers.adb
package body Registers is
procedure Core_Reg_Write
(Target : not null Register_Access;
Value : Interfaces.Unsigned_8)
is begin
Target.Value := Value;
end Core_Reg_Write;
function Core_Reg_Read
(Source : not null Register_Access)
return Interfaces.Unsigned_8
is begin
return Source.Value;
end Core_Reg_Read;
end Registers;
io_registers.ads
with Registers;
-- Specific Register types and functionality
package IO_Registers with Pure is
-- Use different ones for each register to avoid accidental copy/paste
-- errors.
type Port_Register is new Registers.Register_Access;
type DD_Register is new Registers.Register_Access;
type Pin_Register is new Registers.Register_Access;
type Pin_Number is new Positive range 1 .. 8;
type GPIO_Register is record
Port_Reg : Port_Register;
DD_Reg : DD_Register;
Pin_Reg : Pin_Register;
Pin : Pin_Number;
end record;
end IO_Registers;
predefined_registers.ads
with Registers;
with System;
package Predefined_Registers is
-- Fake addresses here, since I don't have your atmega package
GPIO_15_Pin_Reg : aliased Registers.Register
with
Address => System'To_Address(16#80000400#),
Volatile,
Convention => C,
Import;
GPIO_15_DD_Reg : aliased Registers.Register
with
Address => System'To_Address(16#80000401#),
Volatile,
Convention => C,
Import;
GPIO_15_Port_Reg : aliased Registers.Register
with
Address => System'To_Address(16#80000402#),
Volatile,
Convention => C,
Import;
GPIO_15_Pin : constant := 1;
end Predefined_Registers;
program.adb
with IO_Registers;
with Predefined_Registers;
procedure Program is
GPIO_15 : IO_Registers.GPIO_Register :=
(Port_Reg => Predefined_Registers.GPIO_15_Port_Reg'Access,
Pin_Reg => Predefined_Registers.GPIO_15_Pin_Reg'Access,
DD_Reg => Predefined_Registers.GPIO_15_DD_Reg'Access,
Pin => Predefined_Registers.GPIO_15_Pin);
begin
-- Notice the use of IO_Registers for this call. The new types were
-- created there, so the corresponding ops were too
IO_Registers.Core_Reg_Write(GPIO_15.Port_Reg,16#01#);
end Program;
After a bit of thought, I decided to follow on what I already do in C. There, I have the following typedef defined
typedef struct {
IO_REG_TypeDef_t portr;
IO_REG_TypeDef_t ddr;
IO_REG_TypeDef_t pinr;
volatile uint8_t pin;
} GPIO_TypeDef_t;
And IO_REG_t is itself defined as
typedef struct {
volatile uint8_t* io_reg;
} IO_REG_TypeDef_t;
So clearly the key parameters for the gpio are lugged around in a typedef. I thought to do the same in Ada. Again, forgive me if I am speaking C in Ada; feel free to suggest more Ada-standard approaches.
I define the gpio pin components:
-- GPIO15 is PB1 on ATmega328p 28 DIP
gpio15_pin_reg : Unsigned_8;
for gpio15_pin_reg'Address use Atmega328p.PINB'Address;
gpio15_dd_reg : Unsigned_8;
for gpio15_dd_reg'Address use Atmega328p.DDRB'Address;
gpio15_port_reg : Unsigned_8;
for gpio15_port_reg'Address use Atmega328p.PORTB'Address;
gpio15_pin : constant Unsigned_8 := 1;
Register read & write functions are defined:
procedure core_reg_write (reg: in out Unsigned_8; value: in Unsigned_8) is
begin
reg := value;
end core_reg_write;
function core_reg_read (reg: in Unsigned_8) return Unsigned_8 is
value : Unsigned_8;
begin
value := reg;
return value;
end core_reg_read;
Then a record is defined, this time, to lug around the pin variable and, instead of variables for the pin, dd and port registers, their addresses instead:
type gpio_t is record
pin : Unsigned_8;
pin_reg_addr : System.Address;
dd_reg_addr : System.Address;
port_reg_addr : System.Address;
end record;
The record for a given gpio pin is assembled:
gpio15 : gpio_t := (gpio15_pin, gpio15_pin_reg'Address, gpio15_dd_reg'Address, gpio15_port_reg'Address);
Procedures that take this record and set parameters of the pin are defined:
procedure gpio_output (gpio : in gpio_t) is
dd_reg : Unsigned_8;
for dd_reg'Address use gpio.dd_reg_addr;
begin
core_reg_write(dd_reg, core_reg_read(dd_reg) or shift_left(1,integer(gpio.pin)));
end gpio_output;
procedure gpio_hi (gpio : in gpio_t) is
port_reg : Unsigned_8;
for port_reg'Address use gpio.port_reg_addr;
begin
core_reg_write(port_reg, core_reg_read(port_reg) or shift_left(1,integer(gpio.pin)));
end gpio_hi;
procedure gpio_lo (gpio : in gpio_t) is
port_reg : Unsigned_8;
for port_reg'Address use gpio.port_reg_addr;
begin
core_reg_write(port_reg, core_reg_read(port_reg) and not shift_left(1,integer(gpio.pin)));
end gpio_lo;
In each of these procedures, the required registers are, for lack of a better description, manually dereferenced.
The following sequence follows the begin keyword:
-- Initialize
gpio_output(gpio15);
For_loop_0:
loop
-- turn on
gpio_hi(gpio15);
-- loop
Lazy_delay_1:
for I in Unsigned_32 range 0 .. 100_000 loop
null;
end loop Lazy_delay_1;
-- turn off
gpio_lo(gpio15);
-- loop
Lazy_delay_2:
for I in Unsigned_32 range 0 .. 100_000 loop
null;
end loop Lazy_delay_2;
end loop For_loop_0;
And the led blinks.
This achieves what I want but I’m open to other approaches that take a composite gpio_t-like type and don’t require manual dereferencing of the address/pointer.
After playing around a bit, in this online compiler (https://www.tutorialspoint.com/compile_ada_online.php) I got this working:
with Ada.Text_IO; use Ada.Text_IO;
with Interfaces; use Interfaces;
with System; use System;
procedure Hello is
-- pseudo hardware registers, unknown addresses, known contents
temp0 : interfaces.unsigned_8 := 2#00000101#; -- pinr
temp1 : interfaces.unsigned_8 := 2#10000000#; -- ddr
temp2 : interfaces.unsigned_8 := 2#10000000#; -- portr
-- core
type io_reg_t is limited record
io_reg : interfaces.unsigned_8;
end record;
pragma volatile(io_reg_t); -- Verify relevance.
-- processor
gpio15_pinr : aliased io_reg_t;
for gpio15_pinr'address use temp0'address;
gpio15_ddr : aliased io_reg_t;
for gpio15_ddr'address use temp1'address;
gpio15_portr : aliased io_reg_t;
for gpio15_portr'address use temp2'address;
gpio15_pin : constant interfaces.unsigned_8 := 1;
procedure core_reg_write_old (reg: in out unsigned_8; value: in unsigned_8) is
begin
reg := value;
end core_reg_write_old;
procedure core_reg_write (reg: access io_reg_t; value: in unsigned_8) is
begin
reg.io_reg := value;
end core_reg_write;
function core_reg_read (reg: access io_reg_t) return Unsigned_8 is
begin
return reg.io_reg;
end core_reg_read;
-- gpio
type gpio_t is record
pinr : access io_reg_t;
ddr : access io_reg_t;
portr : access io_reg_t;
pin : interfaces.unsigned_8;
end record;
pragma volatile(gpio_t); -- Verify relevance.
procedure gpio_output (gpio : in gpio_t) is
begin
core_reg_write(gpio.ddr,core_reg_read(gpio.ddr) or shift_left(1,integer(gpio.pin)));
end gpio_output;
procedure gpio_hi (gpio : in gpio_t) is
begin
core_reg_write(gpio.portr,core_reg_read(gpio.portr) or shift_left(1,integer(gpio.pin)));
end gpio_hi;
procedure gpio_lo (gpio : in gpio_t) is
begin
core_reg_write(gpio.portr,core_reg_read(gpio.portr) and not shift_left(1,integer(gpio.pin)));
end gpio_lo;
gpio15 : gpio_t := (
pinr => gpio15_pinr'access,
ddr => gpio15_ddr'access,
portr => gpio15_portr'access,
pin => gpio15_pin
);
-- led
type led_t is record
gpio : gpio_t;
end record;
led0 : led_t := (gpio => gpio15);
procedure led_init (led : in led_t) is
begin
gpio_output(led.gpio);
end led_init;
procedure led_on (led : in led_t) is
begin
gpio_hi(led.gpio);
end led_on;
procedure led_off (led : in led_t) is
begin
gpio_lo(led.gpio);
end led_off;
begin
put_line("Hello, world!");
-- Does it match the original value of 5?
put_line(gpio15.pinr.io_reg'Image);
-- Does modification via variable alter the value returned?
temp0 := 203;
put_line(gpio15.pinr.io_reg'Image);
-- Does modification via record alter the value returned?
gpio15.pinr.io_reg := 89;
put_line(gpio15.pinr.io_reg'Image);
-- Writes value in temp2 (128) to temp0.
core_reg_write_old(temp0,temp2);
put_line(gpio15.pinr.io_reg'Image);
put_line(gpio15.ddr.io_reg'Image);
put_line(gpio15.portr.io_reg'Image);
put_line(gpio15.pin'Image);
-- Writes value of pin (1) to pinr via record.
--core_reg_write(gpio15.ddr,gpio15.pin);
-- Writes 1 shifted value of pin times and or's that with ddr reg
--gpio_output(gpio15);
led_init(led0);
put_line(gpio15.pinr.io_reg'Image);
put_line(gpio15.ddr.io_reg'Image);
put_line(gpio15.portr.io_reg'Image);
put_line(gpio15.pin'Image);
--gpio_hi(led0.gpio);
led_on(led0);
put_line(gpio15.pinr.io_reg'Image);
put_line(gpio15.ddr.io_reg'Image);
put_line(gpio15.portr.io_reg'Image);
put_line(gpio15.pin'Image);
--gpio_lo(led0.gpio);
led_off(led0);
put_line(gpio15.pinr.io_reg'Image);
put_line(gpio15.ddr.io_reg'Image);
put_line(gpio15.portr.io_reg'Image);
put_line(gpio15.pin'Image);
end Hello;
I modified this for my embedded environment but it failed to compile, with the complaint:
undefined reference to `__gnat_last_chance_handler’
for the lines “reg.io_reg := value” and “return reg.io_reg”.
I found out that I actually didn’t need the last_chance_handler if my access types were explicitly declared to be “not null”.
So the updated program became:
with Interfaces; use Interfaces;
with System;
with Atmega328p;
procedure Main is
-- core
type io_reg_t is limited record
io_reg : interfaces.unsigned_8;
end record;
pragma volatile(io_reg_t); -- Verify relevance.
type dd_io_reg_t is new io_reg_t;
-- Location?
gpio15_pinr : aliased io_reg_t;
for gpio15_pinr'address use Atmega328p.PINB'Address;
gpio15_ddr : aliased io_reg_t;
for gpio15_ddr'address use Atmega328p.DDRB'Address;
gpio15_portr : aliased io_reg_t;
for gpio15_portr'address use Atmega328p.PORTB'Address;
gpio15_pin : constant interfaces.unsigned_8 := 1;
procedure core_reg_write (reg: not null access io_reg_t; value: in interfaces.unsigned_8) is
begin
reg.io_reg := value;
end core_reg_write;
function core_reg_read (reg: not null access io_reg_t) return interfaces.unsigned_8 is
begin
return reg.io_reg;
end core_reg_read;
-- gpio
type gpio_t is record
pinr : not null access io_reg_t;
ddr : not null access io_reg_t;
portr : not null access io_reg_t;
pin : interfaces.unsigned_8;
end record;
pragma volatile(gpio_t); -- Verify relevance.
-- gpio_output
procedure gpio_output (gpio : in gpio_t) is
begin
core_reg_write(gpio.ddr,core_reg_read(gpio.ddr) or shift_left(1,integer(gpio.pin)));
end gpio_output;
procedure gpio_hi (gpio : in gpio_t) is
begin
core_reg_write(gpio.portr,core_reg_read(gpio.portr) or shift_left(1,integer(gpio.pin)));
end gpio_hi;
procedure gpio_lo (gpio : in gpio_t) is
begin
core_reg_write(gpio.portr,core_reg_read(gpio.portr) and not shift_left(1,integer(gpio.pin)));
end gpio_lo;
gpio15 : gpio_t := (
pinr => gpio15_pinr'access,
ddr => gpio15_ddr'access,
portr => gpio15_portr'access,
pin => gpio15_pin
);
-- led
type led_t is record
gpio : gpio_t;
end record;
led0 : led_t := (gpio => gpio15);
procedure led_init (led : in led_t) is
begin
gpio_output(led.gpio);
end led_init;
procedure led_on (led : in led_t) is
begin
gpio_hi(led.gpio);
end led_on;
procedure led_off (led : in led_t) is
begin
gpio_lo(led.gpio);
end led_off;
begin
-- Initialize
-- Writes value of pin (1) to pinr via record.
--core_reg_write(gpio15.ddr,gpio15.pin);
-- Writes 1 shifted value of pin times and or's that with ddr reg
--gpio_output(gpio15);
led_init(led0);
For_loop_0:
loop
-- turn on
--gpio_hi(led0.gpio);
led_on(led0);
-- loop
Lazy_delay_1:
for i in interfaces.unsigned_32 range 0 .. 100_000 loop
null;
end loop Lazy_delay_1;
-- turn off
--gpio_lo(led0.gpio);
led_off(led0);
-- loop
Lazy_delay_2:
for i in interfaces.unsigned_32 range 0 .. 100_000 loop
null;
end loop Lazy_delay_2;
end loop For_loop_0;
end Main;
After this modification I compiled it burned it into the microcontroller.
And the led blinks.
I'll use this moving forward.

How to use generic parameters that depend on other generic parameters for entities?

I am trying to convert some Verilog code that produces a slower clock from a faster clock for a UART module. The original verilog code is based on the module over at fpga4fun.com, and this is my attempt to translate it for my VHDL-based design.
entity baud_generator is
generic(
f_clk : integer := 50000000; -- default: 50 MHz
baud : integer := 115200; -- default: 115,200 baud
accum_width : integer := 16;
accum_inc : integer := (baud sll accum_width) / f_clk
);
port(
clock : in std_logic;
reset_n : in std_logic;
enable : in std_logic;
baud_clock : out std_logic
);
end entity baud_generator;
However, my compiler, Aldec-HDL, doesn't like the following line:
accum_inc : natural := (baud sll accum_width) / f_clk
Here is the exact error message:
# Error: COMP96_0300: baud_generator.vhd : (20, 52): Cannot reference "f_clk" until the interface list is complete.
# Error: COMP96_0300: baud_generator.vhd : (20, 28): Cannot reference "baud" until the interface list is complete.
# Error: COMP96_0071: baud_generator.vhd : (20, 28): Operator "sll" is not defined for such operands.
# Error: COMP96_0104: baud_generator.vhd : (20, 27): Undefined type of expression.
# Error: COMP96_0077: baud_generator.vhd : (20, 27): Assignment target incompatible with right side. Expected type 'INTEGER'.
In verilog, I have something like this:
module baud_generator(
input clock,
input reset_n,
input enable,
output baud_clock
);
parameter f_clock = 50000000;
parameter baud = 115200;
parameter accum_width = 16;
parameter accum_inc = (baud << accum_width) / f_clock;
//...
endmodule
What is it that I need to modify in that line to make the compiler happy? Is it possible to use generics chained together like that?
This basically says you cannot do computations with the generic values to caluclate (default values for) other generics.
Just use accum_inc as a constant, not as a generic.
Also, the SLL (shift logic left) operator is meant for bit patterns (unsigned and signed datatypes in the ieee.numeric_std and ieee.numeric_bit packages), not for integers. You can do the same by multiplying by a power of two.
It looks to me like accum_inc is a constant, not a parameter (as it's calculated from the generics, so there's no reason to override it)
So it doesn't want to be in the generic part - simply move it to the architecture and make it a constant (and as Philippe noted, do your shifting with multiplies):
constant accum_inc : integer := (baud * (2**accum_width)) / f_clk;
You may find that you overflow what integers can manage, depending on the values of the generics, so you might find you want to use unsigned vectors in the generics and/or calculation.

Resources