bluespec.com Forum Index bluespec.com
Bluespec Forums
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Numeric type definition

 
Post new topic   Reply to topic    bluespec.com Forum Index -> Designing with BSV's Rules, Interfaces, ...
View previous topic :: View next topic  
Author Message
NoOne



Joined: 30 Nov 2012
Posts: 5

PostPosted: Thu Dec 27, 2012 12:10 am    Post subject: Numeric type definition Reply with quote

Hi,
I was wondering if there was any way to define a numeric type as an explicit parameter to a module.
I know I can use a "fake" size n bit vector for example as a parameter to implicitly pass on the size, but that doesn't solve the problem of using static elaboration to actually create varying sizes of this fake parameter.

Basically, is there anyway to get something along the lines of:
Code:
module foo#(numeric n)(Ifc_foo)
    Bit#(n) b = '1;
endmodule


and/or use numeric types in static elaboration such as:
Code:
for (numeric i=0; i < 10; i=i+1) begin
    Bit#(i) b ='1;
    $display("value = %b", b);
end


As I see it, everything is known during compile time, so that shouldn't be the issue.
So is there a way to get some similar behavior so modules can be more generally defined in terms of size of internal structures?

Thanks!
Back to top
View user's profile Send private message
quark
Site Admin


Joined: 02 Nov 2007
Posts: 499

PostPosted: Thu Dec 27, 2012 1:56 pm    Post subject: Re: Numeric type definition Reply with quote

Can you explain what it is that you're trying to do? Maybe I can suggest a different way to write the code, that avoids the need for this feature.

This is not possible:
Code:
module foo#(numeric n)(Ifc_foo);

However, it's equivalent to either of these two options:
Code:
module foo(Ifc_foo#(n));
...
module foo#(Bit#(n) p)(Ifc_foo);

We could support your syntax, but the real issue (regardless of the module syntax) is that there's no way to generate multiple numeric types at the place where you instantiate the module. This code is not possible:
Code:
for (numeric i=0; i < 10; i=i+1)

It may be possible to workaround this limitation by using Integer instead of a numeric type:
Code:
for (Integer i=0; i<10; i=i+1)
   Ifc s <- foo(i);
...
module foo#(Integer i)(Ifc);
   List#(Reg#(Bit#(1))) regs <- replicate(i, mkReg(0));
endmodule

In the above example, we're instantiating a parameterized number of 1-bit registers. This avoids needing to know the length at type-check, and defers it until later in the compilation.

If you do want to keep the parameter in the type domain (and not in the value domain, with Integer), then you might be able to use typeclasses. A typeclass can have a member that is a module, with a numeric type parameter. And the module can iteratively instantiate the same module, but at smaller sizes. Using overlapping instances, you can define a base instance for 0, and an iterative instance for "i". ... Of course, this depends on what it is that you're trying to do.
Back to top
View user's profile Send private message
NoOne



Joined: 30 Nov 2012
Posts: 5

PostPosted: Thu Dec 27, 2012 5:19 pm    Post subject: Thanks Reply with quote

Hi Quark,
Thank you very much for your reply.
Your last example actually solves many of my issues, I wasn't aware you could use a List in a synthesizable module. That indeed lets me pass an Integer as an argument and use it, unlike the size definition for Bit and Vector.

Let me try and give an example where there is still an issue as I see it.
Suppose we have a simple module which should implement a generic counter of size N bits.
If it is just this module I could pass a numeric type N through the interface, or even transfer the counter register itself as a parameter to the module.
But lets say I wanted a higher level module to instantiate several different counters, lets say M counters with size 1..M bits each and this module in itself should be generic in M.
If I used the interface to pass the variable I won't be able to group all my counters in a list or vector. And if I want to use the parameter option then I don't see a way to make it generic.

The option I can think of is to use a list of conditionals, which will limit the highest value M can have (and is pretty awkward and crued overall).

So, something like this:
Code:

package Tb;

import Vector::*;

module genericCounter#(Reg#(Bit#(n)) counter)(Empty);
   rule r1;
      $display("Counting %0d/%0d", counter, valueOf(TExp#(n))-1);
      counter <= counter + 1;
   endrule
endmodule: genericCounter

interface MC_ifc#(numeric type m);
endinterface

module manyCounters(MC_ifc#(m));
   Integer valM = valueOf(m);
   Vector#(m, Empty) counters = newVector;
   
   if (valM > 0) begin
      Reg#(Bit#(1)) r0 <- mkReg(0);
      counters[0] <- genericCounter(r0);
   end
   if (valM > 1) begin
      Reg#(Bit#(2)) r1 <- mkReg(0);
      counters[1] <- genericCounter(r1);
   end
   if (valM > 2) begin
      Reg#(Bit#(3)) r2 <- mkReg(0);
      counters[2] <- genericCounter(r2);
   end
   if (valM > 3) begin
      Reg#(Bit#(4)) r3 <- mkReg(0);
      counters[3] <- genericCounter(r3);
   end
        // and so on and so on...

endmodule: manyCounters

module mkTb(Empty);
   MC_ifc#(3) counters <-   manyCounters;
   Reg#(int) step <- mkReg(0);

   rule tick;
      step <= step + 1;
      if (step > 40)
         $finish;
   endrule
endmodule: mkTb   

endpackage


Your comment on using a typeclass seems interesting, but I'm not sure I followed it completely, I'll have to play around with that to see.

Thanks again for the help.
Back to top
View user's profile Send private message
quark
Site Admin


Joined: 02 Nov 2007
Posts: 499

PostPosted: Fri Dec 28, 2012 2:17 pm    Post subject: Re: Thanks Reply with quote

I will have to think about this example. What's still unclear to me is, once you've instantiate the M counters, do you ever plan to access them? In your example, they have their own rules that run and nothing in the parent module ever needs to access a particular counter, to call methods on it. That would influence how I might write the code.

For instance, in this example, a numeric type parameter "n" does not need to be part of the interface, because the counter does not have a method that is returning a Bit#(n) value. (In your example, it has no methods.) I could imagine an example where each counter is instantiated with type "n" but returns an interface of size "M" -- say, you intend every counter to have a method for comparing a Bit#(M) value against the count (which might be less bits than M).

In your example, where the counter has no interface, in the worst case you could just write a Verilog module and import that into BSV, thus hiding the type issues from BSC altogether. You could import it as a module that takes an Integer:
Code:
import "BVI" Counter =
module mkCounter #(Integer width) (Empty);

And this could be written in Verilog because Verilog supports using parameters to size registers:
Code:
module Counter(CLK, RST)
   parameter width = 1;
   ...
   reg [width-1 : 0] count;
   ...
endmodule

The type issue arises when something in the port list of this module needs to have the Integer "n" in its type. And, of course, you'd prefer to write the code in BSV and not import a Verilog module.

Anyway ... typeclasses might help. In your example, where the counter is not accessed from outside, you could do this:
Code:
interface CountersIfc#(numeric type n);
endinterface

typeclass Counters#(numeric type n);
   module mkCounters(CountersIfc#(n));
endtypeclass

// base case
instance Counters#(0);
   module mkCounters(CountersIfc#(0));
      // do nothing
   endmodule
endinstance

// iterate
instance Counters#(n)
  provisos (Add#(n_minus_1, 1, n),
            Counters#(n_minus_1));
   module mkCounters(CountersIfc#(n));
      // instantiate the smaller counters
      // (use an underscore to keep the RTL instance names short)
      CountersIfc#(n_minus_1) _cs <- mkCounters;

      // instantiate this counter
      Reg#(Bit#(n)) counter <- mkReg(0);
      rule r1;
         $display("Counting %0d/%0d", counter, valueOf(TExp#(n))-1);
         counter <= counter + 1;
      endrule
   endmodule
endinstance

(* synthesize *)
module mkTb(Empty);
   CountersIfc#(3) counters <- mkCounters;
   Reg#(int) step <- mkReg(0);

   rule tick;
      step <= step + 1;
      if (step > 40)
         $finish;
   endrule
endmodule: mkTb

This code synthesizes and appears to do what you intended in your example.

Finally ... there is also a package in the release called HList, which allows you to create a list whose elements are of different types. This type might be helpful, but it might also be a good example to look at -- the source code for this package is available in the release and it uses typeclasses to implement the operations on the HList.
Back to top
View user's profile Send private message
NoOne



Joined: 30 Nov 2012
Posts: 5

PostPosted: Sun Dec 30, 2012 6:17 pm    Post subject: Reply with quote

Thanks for the reply.
Your example helped clarify the recursive use of the typeclass definition.
The example I gave was just to have a simple illustration of the issue, I can easily expand it to have access to the counters, for example, have a simple "read" method which returns the counter's value which the top module can use to make conditional decisions.

I have solved all of the issues I had by using these examples, but they still seem to me like workarounds for something which I feel should be possible in the language.
For example, your Counters example will always produce 1..M counters, what if tomorrow I need only even numbered counters, or say counters which are each bigger by 5 bit than the previous.
If there was a way to pass a numeric type as a parameter, or use a numeric type in for loops you could use static elaboration to solve this, but at the current state, each scenario requires tailoring a custom made solution for it. It just seems to me that this should be solvable somehow since all the required information is present in compile time, there shouldn't be an issue with resolving sizes of bit vectors and so forth.

In any case, thanks for your help, the many examples allowed me to make a specific solution which worked for my problem.
Back to top
View user's profile Send private message
quark
Site Admin


Joined: 02 Nov 2007
Posts: 499

PostPosted: Mon Jan 14, 2013 6:35 pm    Post subject: Reply with quote

Yes. In a typical programming language that has static typing, the type-check is done at compile time and that prevents errors from occurring when you later run the compiled program. If the language did dynamic type-checking, then the errors would be reported at run-time of the program.

BSV, though, is a two-level language: You compile the first level, and then run the first level, and that generates the RTL (which is the second level, that gets run when you simulate). So BSV differs from typical languages in that there is no pause between the compile (and type-check) stage and the execution of the program (the first level part).

So for BSV, the benefit of static type-checking over dynamic type-checking is reduced. Many of the checking that is done with numeric types could be checked at elaboration time. So, yes, I agree that it should be possible to make changes in how numeric types are handled.

I'm not entirely sure how to support both options, though, since there is plenty of legacy code.

There do exist separate types, Vector#(n,t) and List#(t). The difference between them being that the size of a List is not fixed at type-check time. So this is one way that BSC gives you the option to delay numeric checking until elaboration time. (And there are conversion functions between List and Vector.)

So, analogously, if we wanted dynamic checking of bit vectors, that means that instead of values of type Bit#(n), we'd want to just have the type be Bit, with no numeric parameter -- and the size would be part of the value and checked at elaboration time. Unfortunately, I don't see how we'd be able to allow both Bit and Bit#(n) to exist in the same code.

We can define a new type, UBit (for "unsized bit"). But then you would need to convert between Bit#(n) and UBit, in the same way that there is an explicit conversion between List and Vector. (I've written this code somewhere, in fact -- I can try to dig it up.) This new type would work for many uses ... up until the point where you wanted to instantiate state that contains a UBit value. Modules like "mkReg" and "mkFIFO" are defined for Bit#(n) and not for UBit. For registers, you can fake it by instantiating "n" 1-bit registers. But it would be nice to provide some native support in BSC for doing this instantiation, for any module. (I guess that also you'd run into a problem when you wanted to use any primitive operators -- so we'd need a way to define "+" and "*" on the type, for instance.)

Anyway, that is the part that will need some thinking, to figure out how we might support it. But this hasn't been a priority, since I haven't seen many situations where people have not been able to write their designs with statically-checked sizes.
Back to top
View user's profile Send private message
hwang



Joined: 20 Nov 2011
Posts: 4

PostPosted: Sat Mar 05, 2016 1:02 pm    Post subject: Re: Numeric type definition Reply with quote

Hi Quark,

I was trying out the example you gave in the above post.
Code:
for (Integer i=0; i<10; i=i+1)
   Ifc s <- foo(i);
...
module foo#(Integer i)(Ifc);
   List#(Reg#(Bit#(1))) regs <- replicate(i, mkReg(0));
endmodule


Is the code still valid in current Bluespec compiler? I got this error message:
Code:

Error line 47, column 34: (T0081)
  Wrong number of arguments in the use of the following function:
    replicate

  The function expects 1 arguments but was used with 2 arguments.

  Expected type:
    function Vector::Vector#(b__, a__) f(a__ x1)

  Inferred type:
    function e__#(List#(Reg#(Bit#(1)))) f(c__ x1, d__ x2)
Back to top
View user's profile Send private message
quark
Site Admin


Joined: 02 Nov 2007
Posts: 499

PostPosted: Mon Mar 07, 2016 2:15 pm    Post subject: Re: Numeric type definition Reply with quote

The issue is that both the Vector package and the List package export a function called 'replicate'. If you're importing both packages, then you'll want to qualify which one you want:
Code:
List::replicate(i, ...)

If you don't qualify the function, BSC will resolve the name according to its import order (which may not match the order that the imports are written in the file) and without using type information.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    bluespec.com Forum Index -> Designing with BSV's Rules, Interfaces, ... All times are GMT - 4 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You can attach files in this forum
You can download files in this forum
bluespec.com topic RSS feed 


Powered by phpBB © 2001, 2005 phpBB Group
Protected by Anti-Spam ACP