How to implement multi-producer multi-consumer single-buffer model? - locking

There is one buffer, multiple producers and multiple consumers. Any producer(only one) can write the buffer when it is empty. Any consumer(only one) can read the buffer when it is full. How to implement such a model?
I tried using pthread_cond_wait and pthread_cond_signal. But I didn't find a solution.

The following multiple producer multiple consumer single buffer implementation is implemented using Ada. The protected object is the buffer. There are two producers and two consumers. The protected object implicitly handles all the locking and condition variables.
The producer task type, consumer task type and protected object are all defined in an Ada package.
The package specification is:
-----------------------------------------------------------------------
-- Producer-consumer with bounded buffer
-----------------------------------------------------------------------
generic
Capacity : Positive;
package Bounded_PC is
task type Producer is
entry set_id(Id : in Positive);
entry Stop;
end Producer;
task type Consumer is
entry set_id(Id : in Positive);
entry Stop;
end Consumer;
end Bounded_PC;
The package body contains the implementation of the task types and the shared buffer.
with Ada.Text_IO; use Ada.Text_IO;
package body Bounded_PC is
subtype Index_T is Positive range 1..Capacity;
type Buf_Array is array (Index_T) of Integer;
------------
-- Buffer --
------------
protected Buffer is
Entry Write(Item : in Integer);
Entry Read(Item : out Integer);
private
Buf : Buf_Array;
Write_Index : Index_T := 1;
Read_Index : Index_T := 1;
Count : Natural := 0;
end Buffer;
protected body Buffer is
entry Write(Item : in Integer) when Count < Capacity is
begin
Buf(Write_Index) := Item;
Write_Index := (Write_Index mod Capacity) + 1;
Count := Count + 1;
end Write;
entry Read(Item : out Integer) when Count > 0 is
begin
Item := Buf(Read_Index);
Read_Index := (Read_Index mod Capacity) + 1;
Count := Count - 1;
end Read;
end Buffer;
--------------
-- Producer --
--------------
task body Producer is
Value : Integer := 0;
Me : Positive;
begin
accept Set_Id(Id : in Positive) do
Me := Id;
end Set_Id;
loop
select
accept Stop;
exit;
else
select
Buffer.Write(Value);
Put_Line("Producer" & Me'Image & " wrote" & Value'Image);
Value := Value + 1;
or
delay 0.001;
end select;
end select;
end loop;
end Producer;
--------------
-- Consumer --
--------------
task body Consumer is
Value : Integer;
Me : Positive;
begin
accept Set_Id(Id : in Positive) do
Me := Id;
end Set_Id;
loop
select
accept Stop;
exit;
else
select
Buffer.Read(Value);
Put_Line("Consumer" & Me'Image & " read" & Value'Image);
or
delay 0.001;
end select;
end select;
end loop;
end Consumer;
end Bounded_PC;
The condition for each entry call is declared in the protected body.
entry Write(Item : in Integer) when Count < Capacity
entry Read(Item : out Integer) when Count > 0
The main procedure for this program is:
with Bounded_PC;
procedure Main is
package Int_Pck is new Bounded_Pc(10);
use Int_Pck;
P1 : Producer;
P2 : Producer;
C1 : Consumer;
C2 : Consumer;
begin
P1.Set_Id(1);
P2.Set_Id(2);
C1.Set_Id(1);
C2.Set_Id(2);
delay 0.01;
P1.Stop;
P2.Stop;
delay 0.01;
C1.Stop;
C2.Stop;
end Main;

I figure out a solution:
bool buffer_empty = true;
// consumer thread:
pthread_mutex_lock(&buffer_lock);
while (buffer_empty) {
pthread_cond_wait(&buffer_full, &buffer_lock);
}
// consume
buffer_empty = true;
pthread_mutex_unlock(&buffer_lock);
// producer thread:
pthread_mutex_lock(&buffer_lock);
while (!buffer_empty) {
pthread_cond_wait(&buffer_full, &buffer_lock);
}
// produce
buffer_empty = false;
pthread_mutex_unlock(&buffer_lock);
pthread_cond_signal(&buffer_full);

Related

Does HelpNDoc Pascal Script support structures?

I am trying to create a structure:
MyTopic
TopicID : String;
HelpID : Integer;
I wanted to create an array of these structures so I could sort them.
I have tried using this type / record syntax but it is failing.
Update
I defined this type and procedure:
type
TMyTopicRecord = record
idTopic : String;
idContextHelp : integer;
End;
procedure GetSortedTopicIDs(aTopics : array of String; size : Integer);
var
aMyTopicRecords : array of TMyTopicRecord;
temp : TMyTopicRecord;
iTopic, i, j : Integer;
begin
// Init the array
SetLength(aMyTopicRecords, size);
// Fill the array with the existing topid ids.
// Get the context ids at the same time.
for iTopic := 0 to size - 1 do
aMyTopicRecords[iTopic].idTopic := aTopics[iTopic];
aMyTopicRecords[iTopic].idContextHelp := HndTopics.GetTopicHelpContext(aTopics[iTopic]);
// Sort the array on context id
for i := size-1 DownTo 1 do
for j := 2 to i do
if (aMyTopicRecords[j-1].idContextHelp > aMyTopicRecords[j].idContextHelp) Then
begin
temp := aMyTopicRecords[j-1];
aMyTopicRecords[j-1] := aMyTopicRecords[j];
aMyTopicRecords[j] := temp;
end;
// Rebuild the original array of topic ids
for iTopic := 0 to size - 1 do
aTopics[iTopic] := aMyTopicRecords[iTopic].idTopic;
end;
The procedure gets called in a loop of the parent function (code snipped):
function GetKeywordsAsHtml(): string;
var
aKeywordList: THndKeywordsInfoArray;
aAssociatedTopics: array of string;
nBlocLevel, nDif, nClose, nCurKeywordLevel, nCurKeywordChildrenCnt: Integer;
nCurKeyword, nCurKeywordTopic: Integer;
nCountAssociatedTopics: Integer;
sCurrentKeyword, sKeywordLink, sKeywordRelated: string;
sKeywordJsCaption: string;
begin
Result := '<ul>';
nBlocLevel := 0;
try
aKeywordList := HndKeywords.GetKeywordList(False);
for nCurKeyword := 0 to length(aKeywordList) - 1 do
begin
sCurrentKeyword := aKeywordList[nCurKeyword].id;
nCurKeywordLevel := HndKeywords.GetKeywordLevel(sCurrentKeyword);
nCurKeywordChildrenCnt := HndKeywords.GetKeywordDirectChildrenCount(sCurrentKeyword);
sKeywordLink := '#';
sKeywordRelated := '[]';
aAssociatedTopics := HndTopicsKeywords.GetTopicsAssociatedWithKeyword(sCurrentKeyword);
nCountAssociatedTopics := Length(aAssociatedTopics);
if nCountAssociatedTopics > 0 then
begin
GetSortedTopicIDs(aAssociatedTopics, nCountAssociatedTopics);
// Code snipped
end;
end;
finally
Result := Result + '</ul>';
end;
end;
The script compiled in the HelpNDoc internal editor with no issues. But when I go to actually build my HTML documentation I encounter a problem:
The HelpNDoc API is explained here.
Is there something wrong with my code?
I decided to go about it a different way and used a simpler technique:
procedure GetSortedTopicIDs(var aTopics : array of String; iNumTopics : Integer);
var
iTopic : Integer;
// List of output
aList: TStringList;
begin
// Init list
aList := TStringList.Create;
// Build a new array of "nnn x"
// - nnn is the help context id
// - x is the topid id
// Note: I know that the context ID values are within the range 0 - 200
for iTopic := 0 to iNumTopics - 1 do
// We pad the context id with 0. We could increase the padding width to
// make the script mre useful
aList.Add(Format('%0.3d %s', [
HndTopics.GetTopicHelpContext(aTopics[iTopic]),
aTopics[iTopic]
]));
// Now we sort the new array (which basically sorts it by context id)
aList.Sort;
// Update original array
for iTopic := 0 to iNumTopics - 1 do
// We ignore the "nnn " part of the string to get just the topic id
aTopics[iTopic] := copy(aList[iTopic],5, length(aList[iTopic])-4);
// Tidy up
aList.Free;
end;
This compiles and I get the sorted array of topic IDs at the end of it. So the pop-up help is now listed as I want.

Implement an interface with class wide operations in Ada 95

I'm creating a program with Ada 95 and I have a problem. Specifically, I'm trying to implement a class which executes functors given as parameters.
The behavior I want to achieve is:
Declare an interface IF with a procedure Execute.
Derive from IF with a class C and implement Execute.
Create a class D which has a field that is an array of IF. Since IF cannot be instantiated, I use an array of access IF.
Instantiate an object of class D giving it several instances of C as parameters.
Call Execute on every instance of C contained in the array of D.
I've been able to implement the above and compile it, but when I execute it I obtain the error accessibility check failed when trying to assign an object of class C to a component of the array in D.
I know that the error I obtain is because the assignment I'm doing can lead to a danging pointer error according to the Ada policies, so my question is what is the proper way to implement this in Ada 95?
The source code is below. The error is raised in the file elevators.adb, in the procedure Add_Event_Handler, I have commented the statement that causes it.
Functors.ads
package Functors is
type IFunctor is abstract tagged null record;
procedure Execute(Self : in out IFunctor) is abstract;
end Functors;
Elevators.ads
with Functors; use Functors;
package Elevators is
NOT_A_FLOOR : constant := -1;
MAX_EVENT_HANDLERS : constant := 255;
type Floor is new Integer range NOT_A_FLOOR .. 4;
type Elevator is private;
subtype Event_Handler is IFunctor'Class; --'
type Event_Handler_Index is new Integer range 0 .. MAX_EVENT_HANDLERS;
type Event_Handers is array(Event_Handler_Index) of access Event_Handler;
function Create_Elevator return Elevator;
procedure Add_Stop_Handler(Self : in out Elevator; Handler : access Event_Handler);
procedure Add_Moving_Handler(Self : in out Elevator; Handler : access Event_Handler);
procedure Add_Called_Handler(Self : in out Elevator; Handler : access Event_Handler);
procedure Add_Button_Pressed_Handler(Self : in out Elevator; Handler : access Event_Handler);
procedure Run_Simulation(Self : in out Elevator);
private
type Elevator is
record
Current_Floor : Floor := 0;
Is_Moving : Boolean := False;
Next_Floor : Floor := NOT_A_FLOOR;
Stop : Event_Handers := (others => null);
Moving : Event_Handers := (others => null);
Called : Event_Handers := (others => null);
Button_Pressed : Event_Handers := (others => null);
end record;
procedure On_Stop(Self : in out Elevator);
procedure On_Moving(Self : in out Elevator);
procedure On_Called(Self : in out Elevator);
procedure On_Button_Pressed(Self : in out Elevator);
procedure Add_Event_Handler(Self : out Event_Handers; Handler : access Event_Handler);
procedure Exec_All_Events(Self : in out Elevator; EH : in Event_Handers);
end Elevators;
Elevators.adb
with Ada.Text_IO; use Ada.Text_IO;
package body Elevators is
function Create_Elevator return Elevator is
elev : Elevator;
begin
return elev;
end;
procedure Add_Stop_Handler(self : in out Elevator; Handler : access Event_Handler) is
begin
Add_Event_Handler(self.Stop, Handler);
end;
procedure Add_Moving_Handler(self : in out Elevator; Handler : access Event_Handler) is
begin
Add_Event_Handler(self.Moving, Handler);
end;
procedure Add_Called_Handler(self : in out Elevator; Handler : access Event_Handler) is
begin
Add_Event_Handler(self.Called, Handler);
end;
procedure Add_Button_Pressed_Handler(self : in out Elevator; Handler : access Event_Handler) is
begin
Add_Event_Handler(self.Button_Pressed, Handler);
end;
procedure Run_Simulation(self : in out Elevator) is
begin
Put_Line("Floor: " & Floor'Image(self.Current_Floor)); --'
self.Next_Floor := 3;
On_Called(self);
On_Moving(self);
On_Stop(self);
end;
procedure On_Stop(self : in out Elevator) is
begin
self.Current_Floor := self.Next_Floor;
self.Is_Moving := False;
self.Next_Floor := NOT_A_FLOOR;
Put_Line("Stopped. Current floor = " & Floor'Image(self.Current_Floor)); --'
Exec_All_Events(self, self.Stop);
end;
procedure On_Moving(self : in out Elevator) is
begin
self.Is_Moving := True;
self.Current_Floor := NOT_A_FLOOR;
Put_Line("Moving to floor " & Floor'Image(self.Next_Floor)); --'
Exec_All_Events(self, self.Moving);
end;
procedure On_Called(self : in out Elevator) is
begin
Put_Line("Calling button pressed (" & Floor'Image(self.Next_Floor) & ")..."); --'
Exec_All_Events(self, self.Moving);
end;
procedure On_Button_Pressed(self : in out Elevator) is
begin
null;
end;
procedure Add_Event_Handler(Self : out Event_Handers; Handler : access Event_Handler) is
I : Event_Handler_Index := Event_Handler_Index'First; --'
begin
while I < Event_Handler_Index'Last loop --'
if Self(I) = null then
Self(I) := Handler; -- ======> The error is raised here <======
exit;
end if;
I := I + 1;
end loop;
end;
procedure Exec_All_Events(self : in out Elevator; EH : in Event_Handers) is
I : Event_Handler_Index := Event_Handler_Index'First; --'
begin
while I < Event_Handler_Index'Last loop --'
if EH(I) /= null then
EH(I).Execute;
end if;
I := I + 1;
end loop;
end;
end Elevators;
main.adb
with Ada.Text_IO; use Ada.Text_IO;
with Functors; use Functors;
with Elevators; use Elevators;
procedure Main is
type My_Functor is new IFunctor with
record
I : Integer := 0;
end record;
overriding
procedure Execute(Self : in out My_Functor) is
begin
Put_Line("Executing functor, I is " & Integer'Image(Self.I)); --'
Self.I := Self.I + 1;
end;
Generic_Functor : aliased My_Functor;
Elev : Elevator := Create_Elevator;
begin
Add_Stop_Handler(elev, Generic_Functor'Access); --'
Add_Moving_Handler(elev, Generic_Functor'Access); --'
Add_Called_Handler(elev, Generic_Functor'Access); --'
Run_Simulation(Elev);
end;
EDIT
I have done the following changes in order to fix the mentioned runtime error, but I still obtain the accessibility check failed.
elevators.ads
...
type Event_Handler_Generic_Ptr is access all Event_Handler;
type Event_Handers is array(Event_Handler_Index) of Event_Handler_Generic_Ptr;
...
elevators.adb
procedure Add_Event_Handler(Self : out Event_Handers; Handler : access Event_Handler) is
I : Event_Handler_Index := Event_Handler_Index'First; --'
begin
while I < Event_Handler_Index'Last loop --'
if Self(I) = null then
-- Notice the casting here
Self(I) := Event_Handler_Generic_Ptr(Handler); -- ======> The error is raised here <======
exit;
end if;
I := I + 1;
end loop;
end;
Since you store a pointer generated with 'Access in Event_Handlers, you must declare it with access all, so that it is a general access type:
type Event_Handers is array(Event_Handler_Index) of access all Event_Handler;
If you miss all, it is a pool-specific access type. See Ada 95 RM, 3.10 Access Types, (8) and (10). pool-specific access types may only hold pointers to objects allocated in a storage pool, which your object is not.

Why I cannot find window?

I use this example to send a string between two applications.
When I press the Send button for the first time, the string is sent to the Receiver, but only a part of the string is received.
When I press the Send button for the second time, I get "Window not found!".
The window is right there on screen. Why it works when I press the button the first time, but not the second time?
This is the sender:
procedure TfrmSender.SendString;
var
stringToSend : string;
copyDataStruct : TCopyDataStruct;
begin
Caption:= 'Sending';
stringToSend := 'About - Delphi - Programming';
copyDataStruct.dwData := 12821676; //use it to identify the message contents
copyDataStruct.cbData := 1 + Length(stringToSend) ;
copyDataStruct.lpData := PChar(stringToSend);
SendData(copyDataStruct) ;
end;
procedure TfrmSender.SendData(CONST copyDataStruct: TCopyDataStruct);
VAR
receiverHandle : THandle;
res : integer;
begin
receiverHandle := FindWindow(PChar('TfrmReceiver'), PChar('frmReceiver')) ;
if receiverHandle = 0 then
begin
Caption:= 'Receiver window NOT found!';
EXIT;
end;
res:= SendMessage(receiverHandle, WM_COPYDATA, Integer(Handle), Integer(#copyDataStruct));
if res= 0 then Caption:= 'Receiver window found but msg not hand';
end;
And this is the receiver:
procedure TfrmReceiver.WMCopyData(var Msg: TWMCopyData);
VAR
s : string;
begin
if Msg.CopyDataStruct.dwData = 12821676 then
begin
s := PChar(Msg.CopyDataStruct.lpData);
msg.Result := 2006; //Send something back
Winapi.Windows.Beep(800, 300);
Caption:= s;
end
end;
To summarize the comments there are two errors
1) (See #Tom Brunberg) is that the length is set incorrectly which is why you only get part (about half? of the string)
It should be
copyDataStruct.cbData := sizeof( Char )*(Length(stringToSend) + 1 );
2) The forms caption is being changed which invalidates the expression
FindWindow(PChar('TfrmReceiver'), PChar('frmReceiver'))
because the second parameter is the form's caption (in Delphi terminology)

How to use Ada 2012 user defined iterators?

Ada 2012 user defined iterator
This feature allows user to make a custom iterator. Instead of writing Get (List, Index) one could write List (Index) and instead of writing for Index in 1 .. List.Max one could write for Index in List'Range. I am not sure if List'Range is possible if list is not an array, maybe there is another syntax for user defined iterator.
How to use Ada 2012 user defined iterators?
Preferably: how to implement user defined iterator in the example?
Example
This is a Stack or LIFO example. Next step is to hide type Stack members and implement user defined iterator for type Stack List.
with Ada.Text_IO;
with Ada.Integer_Text_IO;
procedure Main is
use Ada.Text_IO;
use Ada.Integer_Text_IO;
type Integer_Array is array (Integer range <>) of Integer;
type Stack (Count : Natural) is record
List : Integer_Array (1 .. Count);
Top : Natural := 0;
end record;
procedure Push (Item : Integer; Result : in out Stack) is
begin
Result.Top := Result.Top + 1;
Result.List (Result.Top) := Item;
end;
S : Stack (10);
begin
Push (5, S);
Push (3, S);
Push (8, S);
for I in S.List'First .. S.Top loop
Put (S.List (I), 2);
New_Line;
end loop;
--for I in S'Range loop
--Put (S (I), 2);
--New_Line;
--end loop;
end;
The use of S'Range is not available for containers, only for standard arrays. This cannot be changed.
You likely meant to say for C in S.Iterate loop which calls the function Iterate and returns cursors for each step of the loop. At that point, you need to use Element (C) to get access to the actual element (or perhaps more efficiently Reference (C).
A third version is for E of S loop which returns directly the element. You do not have access to the corresponding cursor. This is in general the preferred way to write the loop, except perhaps when iterating over the whole contents of a map, since there is no way to access the key, only the value.
For more information on how to add support for these loops in your own data structures, you could take a look at the two gems published by AdaCore:
http://www.adacore.com/adaanswers/gems/gem-127-iterators-in-ada-2012-part-1/
http://www.adacore.com/adaanswers/gems/gem-128-iterators-in-ada-2012-part-2/
which should provide all the information you need, I hope.
I'm not sure that being able to iterate over the contents of a stack is part of the normal use case for stacks! That aside, this is the sort of code you can write using generalized iteration (ARM 5.5.2):
with Ada.Text_IO;
with Stacks;
procedure Iteration is
use Ada.Text_IO;
S : Stacks.Stack (10);
begin
Stacks.Push (S, 5);
Stacks.Push (S, 3);
Stacks.Push (S, 8);
for C in Stacks.Iterate (S) loop
Put_Line (Stacks.Element (C)'Img); --'
end loop;
end Iteration;
This is a possible spec for Stacks. Note that System is only used in the private part.
with Ada.Iterator_Interfaces;
private with System;
package Stacks is
These are part of the fundamental Stack abstraction. Note that if you wanted to be able to write an indexed access (My_Stack (42) or the loop for E of My_Stack loop...) things get much more complicated. For a start, Stack would have to be tagged.
type Stack (Count : Natural) is private;
procedure Push (To : in out Stack; Item : Integer);
function Element (S : Stack; Index : Positive) return Integer;
The remainder of the public part is to support generalized iteration.
type Cursor is private;
function Has_Element (Pos : Cursor) return Boolean;
function Element (C : Cursor) return Integer;
package Stack_Iterators
is new Ada.Iterator_Interfaces (Cursor, Has_Element);
function Iterate (S : Stack)
return Stack_Iterators.Forward_Iterator’Class; --'
private --'
type Integer_Array is array (Positive range <>) of Integer;
type Stack (Count : Natural) is record
List : Integer_Array (1 .. Count);
Top : Natural := 0;
end record;
function Element (S : Stack; Index : Positive) return Integer
is (S.List (Index));
Cursor needs a reference to the object it's a cursor for. I've used System.Address to avoid using access types and needing to make Stacks aliased.
type Cursor is record
The_Stack : System.Address;
List_Index : Positive := 1;
end record;
end Stacks;
The package body ...
with System.Address_To_Access_Conversions;
package body Stacks is
procedure Push (To : in out Stack; Item : Integer) is
begin
To.Top := To.Top + 1;
To.List (To.Top) := Item;
end Push;
We're going to have to convert Stack addresses to pointers-to-Stacks.
package Address_Conversions
is new System.Address_To_Access_Conversions (Stack);
function Has_Element (Pos : Cursor) return Boolean is
S : Stack renames Address_Conversions.To_Pointer (Pos.The_Stack).all;
begin
return Pos.List_Index <= S.Top;
end Has_Element;
function Element (C : Cursor) return Integer is
S : Stack renames Address_Conversions.To_Pointer (C.The_Stack).all;
begin
return S.List (C.List_Index);
end Element;
Now for the actual iterator; I've only done the forward version.
type Iterator is new Stack_Iterators.Forward_Iterator with record
The_Stack : System.Address;
end record;
overriding function First (Object : Iterator) return Cursor;
overriding function Next (Object : Iterator; Pos : Cursor) return Cursor;
function Iterate (S : Stack)
return Stack_Iterators.Forward_Iterator'Class is --'
begin
-- I seem to be relying on S being passed by reference.
-- This would be OK anyway if Stack was tagged; but it is a
-- reasonable bet that the compiler will pass a large object
-- by reference.
return It : Iterator do
It.The_Stack := S’Address; --'
end return; --'
end Iterate;
function First (Object : Iterator) return Cursor is
begin
return C : Cursor do
C.The_Stack := Object.The_Stack;
C.List_Index := 1;
end return;
end First;
function Next (Object : Iterator; Pos : Cursor) return Cursor is
pragma Unreferenced (Object);
begin
return C : Cursor do
C.The_Stack := Pos.The_Stack;
C.List_Index := Pos.List_Index + 1;
end return;
end Next;
end Stacks;
You use the iterators in for loops like this:
for C in S.Iterator loop
Put (S (C));
New_Line;
end loop;
Or in the more lazy version:
for E of S loop
Put (E);
New_Line;
end loop;
Implementing iterators is a rather long story, and I refer you to the Ada 2012 rationale (which #trashgod also mentioned) for a detailed explanation.

VHDL - init std_logic_vector array from HEX file

I have simple "RAM" implemented as:
type memory_array is array(31 downto 0) of std_logic_vector(7 downto 0);
signal ram : memory_array;
I would like to init it's content from HEX file. I wonder about reading the file like:
ram_init: process
file file_ptr : text;
variable line_text : string(1 to 14);
variable line_num : line;
variable lines_read : integer := 0;
variable char : character;
variable tmp_hexnum : string(1 to 2);
begin
file_open(file_ptr,"../RAM.HEX",READ_MODE);
while (not endfile(file_ptr)) loop
readline (file_ptr,line_num);
READ (line_num,line_text);
if (lines_read < 32) then
tmp_hexnum := line_text(10 to 11);
-- ram(lines_read) <= tmp_hexnum;
lines_read := lines_read + 1;
wait for 10 ns;
end if;
end loop;
file_close(file_ptr);
wait;
end process;
The problem is (if this code above would works, which I don't even know), how to convert the tmp_hexnum string to std_logic_vector.
Please have patience with me, VHDL beginner.
The first mistake is to use a process : if you attempt to synthesise the design, the process won't do anything until the design is built and running; which is far too late to read a file!
Instead, wrap the init code in a function, and use that to initialise the memory
signal ram : memory_array := my_ram_init(filename => "../RAM.HEX");
This will work in simulation, and many synthesis tools will infer a RAM and initialise it correctly. If you declared a constant instead of a signal, this would create a ROM instead of a RAM.
Anyway the function looks a bit like
function my_ram_init(filename : string) return memory_array is
variable temp : memory_array;
-- other variables
begin
file_open(...);
-- you have a good handle on the function body
file_close(...);
return temp;
end function;
leaving you with the original problem :
temp(lines_read) <= to_slv(tmp_hexnum);
writing the to_slv function. There ought to be a standard library of these, but for some reason there isn't a universally accepted one. So, here's a start...
function to_slv (tmp_hexnum : string) return std_logic_vector is
variable temp : std_logic_vector(7 downto 0);
variable digit : natural;
begin
for i in tmp_hexnum'range loop
case tmp_hexnum(i) is
when '0' to '9' =>
digit := Character'pos(tmp_hexnum(i)) - Character'pos('0');
when 'A' to 'F' =>
digit := Character'pos(tmp_hexnum(i)) - Character'pos('A') + 10;
when 'a' to 'f' =>
digit := Character'pos(tmp_hexnum(i)) - Character'pos('a') + 10;
when others => digit := 0;
end case;
temp(i*4+3 downto i*4) := std_logic_vector(to_unsigned(digit));
end loop;
return temp;
end function;
Converting a string of variable length to std_logic_vector with length as 4 *
length of string, can be done with the function below:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
...
-- Convert string to std_logic_vector, assuming characters in '0' to '9',
-- 'A' to 'F', or 'a' to 'f'.
function str_to_slv(str : string) return std_logic_vector is
alias str_norm : string(1 to str'length) is str;
variable char_v : character;
variable val_of_char_v : natural;
variable res_v : std_logic_vector(4 * str'length - 1 downto 0);
begin
for str_norm_idx in str_norm'range loop
char_v := str_norm(str_norm_idx);
case char_v is
when '0' to '9' => val_of_char_v := character'pos(char_v) - character'pos('0');
when 'A' to 'F' => val_of_char_v := character'pos(char_v) - character'pos('A') + 10;
when 'a' to 'f' => val_of_char_v := character'pos(char_v) - character'pos('a') + 10;
when others => report "str_to_slv: Invalid characters for convert" severity ERROR;
end case;
res_v(res_v'left - 4 * str_norm_idx + 4 downto res_v'left - 4 * str_norm_idx + 1) :=
std_logic_vector(to_unsigned(val_of_char_v, 4));
end loop;
return res_v;
end function;
Your (both) answers helped me a lot. But it seems not working.
function ram_init(filename : string) return memory_array is
variable temp : memory_array;
file file_ptr : text;
variable line_line : line;
variable line_text : string(1 to 14);
variable tmp_hexnum : string(1 to 2);
variable lines_read : integer := 0;
begin
file_open(file_ptr,filename,READ_MODE);
while (lines_read < 32 and not endfile(file_ptr)) loop
readline (file_ptr,line_line);
read (line_line,line_text);
tmp_hexnum := line_text(10 to 11);
temp(lines_read) := hex_to_bin(tmp_hexnum);
lines_read := lines_read + 1;
end loop;
file_close(file_ptr);
return temp;
end function;
signal ram : memory_array := ram_init(filename=>"../RAM.HEX");
If I set tmp_hexnum to e.g. "0A", it's OK, but reading from file do not fill the RAM.
Can you please check the file part for me, too?