I am working with interfaces array used to bind parametrized modules.
My problem is that I cannot directly concatenate several simple interfaces into an array of interfaces.
Here is a light version of the interface that I use:
interface channel ();
wire req;
wire ack;
modport in (input req output ack);
modport out(input ack output req);
endinterface
What I want to do would be:
Channel array[2]();
Channel elem0();
Channel elem1();
"assign" array = '{elem0,elem1};
I found a workaround using a module that assigns each wire independently:
module passthrough (Channel.in from, Channel.out to );
assign to.req = from.req;
assign from.ack = to.ack;
endmodule
Using it, I have to 'cast' each member element into each array member. Using the previous example:
passthrough pass0( .from(elem0), .to(array[0]));
passthrough pass1( .from(elem1), .to(array[1]));
I do not like it though as the primary idea about using parametrized modules was to make my RTL clearer, shorter and maintainable.
I naturally put the instantiation of this passthroughmodule in a macro but still, I am not satisfied.
I then stumbled on tasks embeded in interfaces. Although I am not quite familiar with tasks in (S)Verilog so I do not think that my idea would work. Here is however what I tried:
interface channel ();
reg req;
reg ack;
modport in (input req output ack);
modport out(input ack output req);
task wrap_from (Channel.in I);
always#* begin
req = I.req;
I.ack = ack;
end
endtask
endinterface
The advantage of this method would be that I would not need to create a module each time I need to connect 2 channels. The code line would also be shorter.
Unfortunately, my compiler(ncsim) does not recognize channel as an interface inside the its own declaration(I guess it needs the endinterface keyword to consider it in the namespace).
I would be extremely grateful if anyone has a cleaner than solution than me for this problem.
One last thing, everything needs to be synthetisable.
I'm struggling with why you want to unroll an array of interfaces. Why do elem0 and elem1 even exist? As suggested, there is no way to "assign" interfaces to each other. My recommendation is to just use Channel array[2] everywhere. You can bind to each instance and you can connect each to a module. Maybe if you shared to what you are trying to connect I could help out more?
I'm doubtful that module tasks are synthesizeable. That looks like a dead end to me.
Related
I'm using the STM BlueNRG-MS chip on my peripheral device and after connection I'd like to read the name of the connected central device (android phone).
I thought I can do this directly in my user_notify routine which is registered as hci callback
/* Initialize the Host-Controller Interface */
hci_init(user_notify, NULL);
So on the EVT_LE_CONN_COMPLETE event, I take the provided handle for the central device and I use aci_gatt_read_using_charac_uuid() to read what I thought is the characteristic with the device name (uuid 0x2a00).
case EVT_LE_META_EVENT:
{
evt_le_meta_event *evt = (void *)event_pckt->data;
switch(evt->subevent){
case EVT_LE_CONN_COMPLETE:
{
evt_le_connection_complete *cc = (void *)evt->data;
GAP_ConnectionComplete_CB(cc->peer_bdaddr, cc->handle);
uint16_t uuid = 0x2a00;
resp = aci_gatt_read_using_charac_uuid(cc->handle, 0, 1, UUID_TYPE_16, (uint8_t*)&uuid);
LOG("GATT read status: %d", resp);
enqueEvent(EVENT_BLE_CONNECTED);
}
break;
}
}
Long story short, it doesn't work. First thing I'm not sure about is, what is the start_handle and end_handle parameter of aci_gatt_read_using_charac_uuid(), it returns ERR_INVALID_HCI_CMD_PARAMS.
Can someone shed some light here?
update
What also puzzles me is that the function aci_gatt_read_using_charac_uuid() is nowehere referenced in the BlueNRG-MS Programming Guidelines.
update2
I changed the function call to aci_gatt_read_using_charac_uuid(cc->handle, 0x0001, 0xffff, UUID_TYPE_16, (uint8_t*)&uuid); but I still get the ERR_INVALID_HCI_CMD_PARAMS. What which paramter could even be invalid? The uuid exists, I can read the device name if I use the BlueNRG GUI with a bluetooth dongle.
update3
Has anyone ever used this function or somehow managed to read a characteristic from a central device? I'd highly appreciate any help or hint.
Here you go, The BlueNRG-MS Bluetooth® LE stack application command interface (ACI) - User manual
page 75 - 4.6.25 Aci_Gatt_Read_Charac_Using_UUID()
and make sure you have called Aci_Gatt_Init()
The user manual is last revised July 2019, the document you link to is from 2018, don't know if this is why ?
The start_handle and end_handle is the range of handles in your service as pictured here -
Here is a discussion to the closest thing I could find to match your question.
As it turned out there are two bugs in the BlueNRG API.
In bluenrg_aci_const.h file the OCF code OCF_GATT_READ_USING_CHARAC_UUID shall be 0x119 instead of 0x109.
And in the implementation of the aci_gatt_read_using_charac_uuid() function, there is a missing setting on event:
rq.event = EVT_CMD_STATUS;
Patching them fixed the issue.
Any chance I could get a tip for proper way to build an agent that could do read multiple points from multiple devices on a BACnet system? I am viewing the actuator agent code trying learn how to make the proper rpc call.
So going through the agent development procedure with the agent creation wizard.
In the init I have this just hard coded at the moment:
def __init__(self, **kwargs):
super(Setteroccvav, self).__init__(**kwargs)
_log.debug("vip_identity: " + self.core.identity)
self.default_config = {}
self.agent_id = "dr_event_setpoint_adj_agent"
self.topic = "slipstream_internal/slipstream_hq/"
self.jci_zonetemp_string = "/ZN-T"
So the BACnet system in the building has JCI VAV boxes all with the same zone temperature sensor point self.jci_zonetemp_string and self.topic is how I pulled them into volttron/config store through BACnet discovery processes.
In my actuate point function (copied from CSV driver example) am I at all close for how to make the rpc call named reads using the get_multiple_points? Hoping to scrape the zone temperature sensor readings on BACnet device ID's 6,7,8,9,10 which are all the same VAV box controller with the same points/BAS program running.
def actuate_point(self):
"""
Request that the Actuator set a point on the CSV device
"""
# Create a start and end timestep to serve as the times we reserve to communicate with the CSV Device
_now = get_aware_utc_now()
str_now = format_timestamp(_now)
_end = _now + td(seconds=10)
str_end = format_timestamp(_end)
# Wrap the timestamps and device topic (used by the Actuator to identify the device) into an actuator request
schedule_request = [[self.ahu_topic, str_now, str_end]]
# Use a remote procedure call to ask the actuator to schedule us some time on the device
result = self.vip.rpc.call(
'platform.actuator', 'request_new_schedule', self.agent_id, 'my_test', 'HIGH', schedule_request).get(
timeout=4)
_log.info(f'*** [INFO] *** - SCHEDULED TIME ON ACTUATOR From "actuate_point" method sucess')
reads = publish_agent.vip.rpc.call(
'platform.actuator',
'get_multiple_points',
self.agent_id,
[(('self.topic'+'6', self.jci_zonetemp_string)),
(('self.topic'+'7', self.jci_zonetemp_string)),
(('self.topic'+'8', self.jci_zonetemp_string)),
(('self.topic'+'9', self.jci_zonetemp_string)),
(('self.topic'+'10', self.jci_zonetemp_string))]).get(timeout=10)
Any tips before I break something on the live system greatly appreciated :)
The basic form of an RPC call to the actuator is as follows:
# use the agent's VIP connection to make an RPC call to the actuator agent
result = self.vip.rpc.call('platform.actuator', <RPC exported function>, <args>).get(timeout=<seconds>)
Because we're working with devices, we need to know which devices we're interested in, and what their topics are. We also need to know which points on the devices that we're interested in.
device_map = {
'device1': '201201',
'device2': '201202',
'device3': '201203',
'device4': '201204',
}
building_topic = 'campus/building'
all_device_points = ['point1', 'point2', 'point3']
Getting points with the actuator requires a list of point topics, or device/point topic pairs.
# we only need one of the following:
point topics = []
for device in device_map.values():
for point in all_device_points:
point_topics.append('/'.join([building_topic, device, point]))
device_point_pairs = []
for device in device_map.values():
for point in all_device_points:
device_point_pairs.append(('/'.join([building_topic, device]),point,))
Now we send our RPC request to the actuator:
# can use instead device_point_pairs
point_results = self.vip.rpc.call('platform.actuator', 'get_multiple_points', point_topics).get(timeout=3)
maybe it's just my interpretation of your question, but it seems a little open-ended - so I shall respond in a similar vein - general (& I'll try to keep it short).
First, you need the list of info for targeting each device in-turn; i.e. it might consist of just a IP(v4) address (for the physical device) & the (logical) device's BOIN (BACnet Object Instance Number) - or if the request is been routed/forwarded on by/via a BACnet router/BACnet gateway then maybe also the DNET # & the DADR too.
Then you probably want - for each device/one at a time, to retrieve the first/0-element value of the device's Object-List property - in order to get the number of objects it contains, to allow you to know how many objects are available (including the logical device/device-type object) - that you need to retrieve from it/iterate over; NOTE: in the real world, as much as it's common for the device-type object to be the first one in the list, there's no guarantee it will always be the case.
As much as the BACnet standard started allowing for the retrieval of the Property-List (property) from each & every object, most equipment don't as-yet support it, so you might need your own idea of what properties (/at least the ones of interest to you) that each different object-type supports; probably at the very-very least know which ones/object-types support the Present-Value property & which ones don't.
One ideal would be to have the following mocked facets - as fakes for testing purposes instead of testing against a live/important device (- or at least consider testing against a noddy BACnet enabled Raspberry PI or the harware-based like):
a mock for your BACnet service
a mock for the BACnet communication stack
a mock for your device as a whole (- if you can't write your own one, then maybe even start with the YABE 'Room Control Simulator' as a starting point)
Hope this helps (in some way).
I'm trying to send a large block of data between applications by sending a control message over DBus from one to the other requesting a Unix file descriptor. I have it so that the client can request this, the server creates a DBus message that includes a UnixFDList, and the client receives a reply message but it doesn't contain anything. On the server side in Vala the DBusConnection object is setup using register_object, unfortunately the Vapi hides the DBusInterfaceVTable parameter that all the C examples use that would let me specify a delegate for method calls. I've tried to use register_object_with_closures instead but I can't seem to get that to work and the Closure object in Vala is woefully undocumented.
It seems to me that I need one of these methods in order to receive the message from the DBusMethodInvocation object that you get from a call to the DBusInterfaceMethodCallFunc delegate, with that you can create a reply message. Is there a way to either specify a closure class that works with register_object_with_closures, or a way to specify a DBusInterfaceVTable object as part of the service data?
I know that one option is to just create the service in C, but I'd rather figure out and understand how this works in Vala.
Vala uses UnixFDList internally for methods that contain a parameter of type GLib.UnixInputStream, GLib.UnixOutputStream, GLib.Socket, or GLib.FileDescriptorBased.
Example:
[DBus(name="eu.tiliado.Nuvola")]
public interface MasterDbusIfce: GLib.Object {
public abstract void get_connection(
string app_id,
string dbus_id,
out GLib.Socket? socket,
out string? token) throws GLib.Error;
}
When trying to use a callback function for a DBus reply I get stuck on creating a good/well working main loop.
What I want to do is simple, make a DBus call and specify a function which should be called when the reply comes. This is because I do not want to block my thread for the whole time until a reply has been calculated and arrived.
I first use dbus_connection_send_with_reply(..) to get a DBusPendingCall, then i specify a callback function using dbus_pending_call_set_notify(..). Before doing this, when connecting to the bus, I have started another thread which should wait for a response and call the callback functions. I have found no examples at all and no very good documentation of how I should design a dbus main-loop like this. I have tried:
// Main dbus loop handling data transfer and callbacks..
void *dbus_main(void *args)
{
my_dbus dbus = (my_dbus)args;
while (MY_DBUS_STATUS_STOPPING != dbus->status
&& dbus_connection_read_write_dispatch(dbus->conn, -1))
;
return 0;
}
// Start the dbus main loop in a new thread
void dbus_main_start(my_dbus dbus) {
if (!pthread_create(&dbus->th, NULL, dbus_main, dbus)) {
// PRINT ERROR
}
}
My problem is two things:
I try to stop the app by setting the dbus->status flag to MY_DBUS_STATUS_STOPPING and waiting for the threads to join. This does not work if the thread is blocked in the dbus_connection_read_write_dispatch(..) function. If i want the app to stop fast then I need to specify a very short timeout. Can't I wake the blocked thread in some other way?
More seriously, with this code i don't get any callback from the method I call. If I add some fprintf(..) to write to stdout I might suddenly get my callback. It seems quite random, so maybe some kind of deadlock? I have tried having a dbus_connection_flush(..) between sending the message and adding the callback with _set_notify(..) function. Doesn't do any difference... But printing some letters to stdout in the same place fixes the problem. Printing to stdout in the dbus-main-loop insted of an empty ";" seems to do the trick sometimes...
So anyone who has an example of using the low-level dbus api together with async methods, ie not using _block(..)??
You can create a simple DBus application as follows...
To setup a server to handle incoming messages, call dbus_connection_register_object_path passing in a VTable containing function pointers to handle the messages. Such as:
{ .unregister_function = UnregisteredMessage, .message_function = ServiceMessage }
To send a new message, call dbus_connection_send_with_reply and then dbus_pending_call_set_notify to associate a callback function to handle the reply.
Next you will need to service DBus. This can be done in a separate thread or by calling periodically with non-blocking calls in the same thread, as shown below:
/* Non-blocking read of the next available message */
dbus_connection_read_write ( MyDBusConnection, 0 ) ;
while ( dbus_connection_get_dispatch_status ( MyDBusConnection ) == DBUS_DISPATCH_DATA_REMAINS )
{
dbus_connection_dispatch ( MyDBusConnection ) ;
}
There are some good example of using the DBUS C API here: http://www.matthew.ath.cx/misc/dbus
It is highly recommended that you use a D-Bus library other than libdbus, as libdbus is fiddly to use correctly, as you are finding. If possible, use GDBus or QtDBus instead, as they are much higher-level bindings which are easier to use. If you need a lower-level binding, sd-bus is more modern than libdbus.
If you use GDBus, you can use GMainLoop to implement a main loop. If you use sd-bus, you can use sd-event.
I've built the next (working correctly) Scoreboard/Monitor environment:
// Scoreboard : like uvm_scoreboard
scbd_port packet_add : add packet_s;
scbd_port packet_match : match packet_s;
My ADD flow:
// Monitor:
expected_packet_o : out interface_port of tlm_analysis of packet_s is instance;
connect_ports() is also {
expected_packet_o.connect(Scoreboard.packet_add);
};
add_to_Scoreboard() is {
// ... collecting packet logic ...
// Actually adding the packet to SB:
expected_packet_o$write(expected_packet);
};
My MATCH flow:
// Monitor:
collect_DUT_output() is {
// ... receiving packet logic ...
Scoreboard.match_in_scbd(received_packet);
};
My questions are: is it the right way Specman's UVM scrb ports should be used?
Why could not I add the expected packet directly through packet_add, something like this: Scoreboard.packet_add$.write(expected_packet)? The only way I've found to add packet to Scoreboard is to connect another TLM port to packet_add, as it is written in the code.
Is there some add method like match_in_scbd in match flow?
Thank you for any clarifications about Specman Scoreboard add and match flows
Think of it this way, the TLM port is just a fancy wrapper of "function pointer" (as in C++) that points to the implemented method. Nothing stop you from calling the implemented method of the instance of the scoreboard instance directly. The only problem is you have to know which instance of the scoreboard to call in the caller.