Commit 95c2bc99 by Zachary Snow

support for common non-ANSI style port declarations

Specifically, support has been added for non-ANSI style port
declarations where the port declaration is separate from the
corresponding net or variable declaration.
parent 407ba590
......@@ -24,6 +24,8 @@
explicitly providing a leading `parameter` or `localparam` marker
* Use UTF-8 on all platforms and tolerate transcoding failures, enabling reading
files encoding using Latin-1 with special characters in comments
* Support for non-ANSI style port declarations where the port declaration is
separate from the corresponding net or variable declaration
## v0.0.8
......
......@@ -38,6 +38,7 @@ import qualified Convert.NamedBlock
import qualified Convert.Package
import qualified Convert.ParamNoDefault
import qualified Convert.ParamType
import qualified Convert.PortDecl
import qualified Convert.RemoveComments
import qualified Convert.ResolveBindings
import qualified Convert.Simplify
......@@ -107,6 +108,7 @@ initialPhases selectExclude =
, selectExclude Job.Assert Convert.Assertion.convert
, selectExclude Job.Always Convert.AlwaysKW.convert
, Convert.Package.convert
, Convert.PortDecl.convert
, Convert.ParamNoDefault.convert
, Convert.ResolveBindings.convert
, Convert.UnnamedGenBlock.convert
......
{- sv2v
- Author: Zachary Snow <zach@zachjs.com>
-
- Conversion for checking and standardizing port declarations
-
- Non-ANSI style port declarations can be split into two separate declarations.
- Section 23.2.2.1 of IEEE 1800-2017 defines rules for determining the
- resulting details of such declarations. This conversion is part of the
- initial phases to avoid requiring that downstream conversions handle these
- unusual otherwise conflicting declarations.
-
- To avoid creating spurious conflicts for redeclared ports, this conversion is
- also responsible for defaulting variable ports to `logic`.
-}
module Convert.PortDecl (convert) where
import Data.List (intercalate, (\\))
import Data.Maybe (mapMaybe)
import Convert.Traverse
import Language.SystemVerilog.AST
convert :: [AST] -> [AST]
convert = map $ traverseDescriptions traverseDescription
traverseDescription :: Description -> Description
traverseDescription (Part attrs extern kw liftetime name ports items) =
Part attrs extern kw liftetime name ports items'
where items' = convertPorts name ports items
traverseDescription other = other
convertPorts :: Identifier -> [Identifier] -> [ModuleItem] -> [ModuleItem]
convertPorts name ports items
| not (null extraPorts) =
error $ "declared ports " ++ intercalate ", " extraPorts
++ " are not in the port list of " ++ name
| otherwise =
map traverseItem items
where
portDecls = mapMaybe (findDecl True ) items
dataDecls = mapMaybe (findDecl False) items
extraPorts = map fst portDecls \\ ports
-- rewrite a declaration if necessary
traverseItem :: ModuleItem -> ModuleItem
traverseItem (MIPackageItem (Decl decl))
| Variable d _ x _ e <- decl = rewrite decl (combineIdent x) d x e
| Net d _ _ _ x _ e <- decl = rewrite decl (combineIdent x) d x e
| otherwise = MIPackageItem $ Decl decl
traverseItem other = other
-- produce the combined declaration for a port, if it has one
combineIdent :: Identifier -> Maybe Decl
combineIdent x = do
portDecl <- lookup x portDecls
dataDecl <- lookup x dataDecls
Just $ combineDecls portDecl dataDecl
-- given helpfully extracted information, update the given declaration
rewrite :: Decl -> Maybe Decl -> Direction -> Identifier -> Expr -> ModuleItem
-- implicitly-typed ports default to `logic` in SystemVerilog
rewrite (Variable d (Implicit sg rs) x a e) Nothing _ _ _ =
MIPackageItem $ Decl $ Variable d (IntegerVector TLogic sg rs) x a e
-- not a relevant port declaration
rewrite decl Nothing _ _ _ =
MIPackageItem $ Decl decl
-- turn the non-ANSI style port and data declarations into fully-specified ports
-- and optional continuous assignments, respectively
rewrite _ (Just combined) d x e
| d /= Local =
MIPackageItem $ Decl combined
| e /= Nil =
Assign AssignOptionNone (LHSIdent x) e
| otherwise =
MIPackageItem $ Decl $ CommentDecl $ "combined with " ++ x
-- combine the two declarations defining a non-ANSI style port
combineDecls :: Decl -> Decl -> Decl
combineDecls portDecl dataDecl
| eP /= Nil =
mismatch "invalid initialization at port declaration"
| aP /= aD =
mismatch "different unpacked dimensions"
| rsP /= rsD =
mismatch "different packed dimensions"
| otherwise =
base (tf rsD) ident aD Nil
where
-- signed if *either* declaration is marked signed
sg = if sgP == Signed || sgD == Signed
then Signed
else Unspecified
-- the port cannot have a variable or net type
Implicit sgP rsP = case tP of
Implicit{} -> tP
_ -> mismatch "redeclaration"
-- pull out the base type, signedness, and packed dimensions
(tf, sgD, rsD) = case tD of
Implicit s r -> (IntegerVector TLogic sg, s, r )
IntegerVector k s r -> (IntegerVector k sg, s, r )
IntegerAtom k s -> (\[] -> IntegerAtom k s , s, [])
-- TODO: other basic types may be worth supporting here
_ -> mismatch "non-ANSI port declaration with unsupported data type"
-- extract the core components of each declaration
Variable dir tP ident aP eP = portDecl
(base, tD, aD) = case dataDecl of
Variable Local t _ a _ -> (Variable dir, t, a)
Net Local n s t _ a _ -> (Net dir n s, t, a)
_ -> undefined -- not possible given findDecl
-- helpful error message utility
mismatch :: String -> a
mismatch msg = error $ "declarations `" ++ p portDecl ++ "` and `"
++ p dataDecl ++ "` are incompatible due to " ++ msg
where p = init . show
-- used to build independent lists of port and data declarations
findDecl :: Bool -> ModuleItem -> Maybe (Identifier, Decl)
findDecl isPort (MIPackageItem (Decl decl))
| Variable d _ x _ _ <- decl, (d /= Local) == isPort = Just (x, decl)
| Net d _ _ _ x _ _ <- decl, (d /= Local) == isPort = Just (x, decl)
findDecl _ _ = Nothing
......@@ -381,9 +381,7 @@ parseDTsAsDecls backupDir mode l0 =
(rs , l6) = takeRanges l5
(tps, l7) = takeTrips l6 initReason
base = von dir t
t = case (dir, tf rs) of
(Output, Implicit sg _) -> IntegerVector TLogic sg rs
(_, typ) -> typ
t = tf rs
decls =
traceComment l0 :
map (\(x, a, e) -> base x a e) tps
......
......@@ -90,6 +90,7 @@ executable sv2v
Convert.Package
Convert.ParamNoDefault
Convert.ParamType
Convert.PortDecl
Convert.RemoveComments
Convert.ResolveBindings
Convert.Scoper
......
`define TEST_SG(port_sg, data_sg, exp_u, exp_s) \
`TEST_RAW(o_``port_sg``_``data_sg``_one, port_sg , data_sg , [ 0:0], exp_u) \
`TEST_RAW(o_``port_sg``_``data_sg``_two, port_sg [1:0], data_sg [1:0] , [ 1:0], exp_u) \
`TEST_RAW(o_``port_sg``_``data_sg``_int, port_sg , integer data_sg, [31:0], exp_s)
`define TEST \
`TEST_SG( , , 0, 1) \
`TEST_SG( , signed, 1, 1) \
`TEST_SG( , unsigned, 0, 0) \
`TEST_SG( signed, , 1, 1) \
`TEST_SG( signed, signed, 1, 1) \
`TEST_SG( signed, unsigned, 1, 0) \
`TEST_SG(unsigned, , 0, 1) \
`TEST_SG(unsigned, signed, 1, 1) \
`TEST_SG(unsigned, unsigned, 0, 0)
`define TEST_RAW(name, a, b, c, d) name,
module top(
`TEST
foo
);
output wire foo;
assign foo = 1;
`undef TEST_RAW
`define TEST_RAW(name, port, data, range, exp) \
`ifdef REF \
output wire range name; \
initial #1 $display(`"name %b %b`", name, 1'b``exp); \
`else \
output port name; \
wire data name; \
initial #1 $display(`"name %b %b`", name, name < 0); \
`endif \
assign name = 1'sb1; \
`TEST
endmodule
`define REF
`include "non_ansi_port_decl.sv"
module mod(a, b, c, d);
input a;
wire a;
output b;
reg b;
always @*
b = ~a;
input [7:0] c;
wire integer d = 10 + c;
logic [7:0] c;
output d;
endmodule
module mod(a, b, c, d);
input wire a;
output reg b;
always @*
b = ~a;
input wire [7:0] c;
output wire [31:0] d;
assign d = 10 + c;
endmodule
module top;
reg a;
wire b;
reg [7:0] c;
wire [31:0] d;
mod m(a, b, c, d);
initial begin
$monitor("%2d %b %b %b %b", $time, a, b, c, d);
#1 a = 0;
#1 a = 1;
for (c = 0; c < 10; c = c + 1)
#1;
end
endmodule
// pattern: declarations `output a = 1` and `logic a` are incompatible due to invalid initialization at port declaration
module top(a);
output a = 1;
logic a;
endmodule
// pattern: declared ports w, z are not in the port list of top
module top(x, y);
output w, x, y, z;
endmodule
// pattern: declarations `output \[1:0\] x` and `wire x` are incompatible due to different packed dimensions
module top(x);
output [1:0] x;
wire x;
endmodule
// pattern: declarations `output x` and `wire \[1:0\] x` are incompatible due to different packed dimensions
module top(x);
output x;
wire [1:0] x;
endmodule
// pattern: declarations `output logic a` and `logic a` are incompatible due to redeclaration
module top(a);
output logic a;
logic a;
endmodule
// pattern: declarations `output x \[1:0\]` and `wire x` are incompatible due to different unpacked dimensions
module top(x);
output x [1:0];
wire x;
endmodule
// pattern: declarations `output x` and `wire x \[1:0\]` are incompatible due to different unpacked dimensions
module top(x);
output x;
wire x [1:0];
endmodule
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment