cgen_vhdl.lua 24.7 KB
Newer Older
twlostow's avatar
twlostow committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
-- -*- Mode: LUA; tab-width: 2 -*-

-- wbgen2, (c) 2010 Tomasz Wlostowski/CERN BE-Co-HT
-- LICENSED UNDER GPL v2

-- File: cgen_vhdl.lua
--
-- The VHDL code generator.
--

-- conversion table between VHDL data types and wbgen2 internal data types
fieldtype_2_vhdl={};
fieldtype_2_vhdl[BIT]="std_logic";
fieldtype_2_vhdl[MONOSTABLE]="std_logic";
fieldtype_2_vhdl[SIGNED] = "signed";
fieldtype_2_vhdl[UNSIGNED] = "unsigned";
fieldtype_2_vhdl[ENUM] = "std_logic_vector";
fieldtype_2_vhdl[SLV] = "std_logic_vector";

20

21 22 23 24 25 26 27 28
function get_pkg_name()
  if (periph.hdl_package) then
      return periph.hdl_package
  else
     return periph.hdl_prefix.."_wbgen2_pkg";
   end
end

twlostow's avatar
twlostow committed
29 30
-- generates a string containing VHDL-compatible numeric constant of value [value] and size [numbits]
function gen_vhdl_bin_literal(value, numbits)
31 32 33 34
 if(numbits == 1) then
	 return string.format("'%d'", csel(value==0,0,1));
 end

twlostow's avatar
twlostow committed
35 36 37 38 39
    local str ='\"';
    local i,n,d,r;
    
    n=value;
    r=math.pow(2, numbits-1);
40 41 42 43 44 45

		if(value == nil) then
	    for i=1,numbits do
				str=str.."X";
			end    
    else
twlostow's avatar
twlostow committed
46 47 48 49 50 51
    for i=1,numbits do
			d=math.floor(n/r);
			str=str..csel(d>0,"1","0");
			n=n%r;
			r=r/2;
    end
52 53
    
    end
twlostow's avatar
twlostow committed
54
    return str..'\"';
55 56 57 58
 end

function strip_periph_prefix(s)
   return string.gsub(s, "^"..periph.hdl_prefix.."\_", "")
twlostow's avatar
twlostow committed
59 60
end

61 62 63 64 65 66 67
function strip_wb_prefix(s)
   local t = string.gsub(s, "^wb\_", "")
    t = string.gsub(t, "_o$","")
    t = string.gsub(t, "_i$","")
    return t
end

68 69
-- fixme: do this neatly
function port2record(s)
70
   if(options.hdl_reg_style == "signals") then
71 72 73 74 75
      return s
   end

   for i,port in ipairs(g_portlist) do
      if(port.name == s and port.is_reg_port) then
76
		      return csel(port.dir=="in", "regs_i.", "regs_o.")..strip_periph_prefix(s)
77
      end
78 79 80 81 82 83 84 85 86

      if(port.name == s and port.is_wb and options.hdl_reg_style == "record_full") then
        if s == "wb_int_o" then
          return "int_o";
        end
          
		      return csel(port.dir=="in", "slave_i.", "slave_o.")..strip_wb_prefix(s)
      end

87 88 89 90
   end
   return s
end

91

92
function cgen_vhdl_package()
93 94
   
   emit("package "..get_pkg_name().." is")
95
   indent_right();
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
   emit("");


   emit("");
   emit("-- Input registers (user design -> WB slave)");
   emit("");
	
	 cgen_vhdl_port_struct("in");

   emit("");
   emit("-- Output registers (WB slave -> user design)");
   emit("");

	 cgen_vhdl_port_struct("out");
	   
 	 local typename = "t_"..periph.hdl_prefix.."_in_registers";

113
   emit("");
114 115
   emit("function \"or\" (left, right: "..typename..") return "..typename..";");
   emit("function f_x_to_zero (x:std_logic) return std_logic;");
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
116
   emit("function f_x_to_zero (x:std_logic_vector) return std_logic_vector;");
117
   emit("");
118

119
   cgen_vhdl_interface_declaration("component")
120
   indent_left();
121

122 123 124
   emit("end package;");
   
   emit("");
125 126
   emit("package body "..get_pkg_name().." is");
    indent_right();
127
   emit("function f_x_to_zero (x:std_logic) return std_logic is");
128
   emit("begin")
129
    indent_right();
130
	 emit("if x = '1' then")
131
    indent_right();
132
   emit("return '1';")
133
    indent_left();
134
   emit("else")
135
    indent_right();
136
	 emit("return '0';")
137
    indent_left();
138
	 emit("end if;")
139
    indent_left();
140
   emit("end function;");
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
141

142 143
    emit("")

Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
144
   emit("function f_x_to_zero (x:std_logic_vector) return std_logic_vector is");
145 146 147
    indent_right();
   emit("variable tmp: std_logic_vector(x'length-1 downto 0);");
    indent_left();
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
148
   emit("begin");
149
    indent_right();
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
150
   emit("for i in 0 to x'length-1 loop");
151
    indent_right();
152
   emit("if(x(i) = '1') then");
153
    indent_right();
154
   emit("tmp(i):= '1';");
155
    indent_left();
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
156
   emit("else");
157
    indent_right();
158
   emit("tmp(i):= '0';");
159
    indent_left();
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
160
   emit("end if; ");
161
    indent_left();
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
162 163
   emit("end loop; ");
   emit("return tmp;");
164
    indent_left();
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
165
   emit("end function;");
166
   emit("");
167
   emit("function \"or\" (left, right: "..typename..") return "..typename.." is");
168
   indent_right();
169
   emit("variable tmp: "..typename..";");
170
   indent_left();
171
   emit("begin");
172
   indent_right();
173 174 175 176 177

   for i=1,table.getn(g_portlist) do
      local port = g_portlist[i];
      if(port.is_reg_port == true and port.dir == "in") then
      	local n = strip_periph_prefix(port.name);
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
178
					emit("tmp."..n.." := f_x_to_zero(left."..n..") or f_x_to_zero(right."..n..");");
179 180 181
      end
   end
	 emit("return tmp;");   
182
   indent_left();
183
   emit("end function;");
184 185
   indent_left();
   emit("");
186 187 188 189 190 191
   emit("end package body;");
end

function cgen_vhdl_port_struct(direction)

   emit("type t_"..periph.hdl_prefix.."_"..direction.."_registers is record");
192 193 194 195 196 197
   indent_right();

   local p_list= {};
   
   for i=1,table.getn(g_portlist) do
      local port = g_portlist[i];
198
      if(port.is_reg_port == true and port.dir == direction) then
199 200 201 202 203
         table.insert(p_list, port);
      end
   end

   for i,port in ipairs(p_list) do
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
204 205
   		local ptype = csel(port.type == SLV and port.range == 1, "std_logic", fieldtype_2_vhdl[port.type]);
      local line = string.format("%-40s : %s", strip_periph_prefix(port.name), ptype);
206 207 208 209 210 211 212 213 214 215

      if(port.range > 1) then
         line = line.."("..(port.range-1).." downto 0)";
      end    

      line = line..";";
      emit(line);
   end

   indent_left();
216
   emit("end record;");
217
   emit("");
218
   emit("constant c_"..periph.hdl_prefix.."_"..direction.."_registers_init_value: t_"..periph.hdl_prefix.."_"..direction.."_registers := (");
219
   indent_right();
220
      
221 222 223 224
   for i=1,table.getn(p_list) do
      local port = p_list[i];
      line = strip_periph_prefix(port.name).." => ";
      if(port.range > 1) then
225 226 227 228 229 230 231 232 233 234
        line = line.."(others => '0')"
          else
        line = line.."'0'"
        end
                if(i ~= table.getn(p_list)) then
              line = line..",";
            end
      
            emit(line);
         end
235 236
   indent_left();
   emit(");");
237 238

end
twlostow's avatar
twlostow committed
239 240

-- function generates a VHDL file header (some comments and library/package include definitions).
241
function cgen_vhdl_header(file_name)
twlostow's avatar
twlostow committed
242 243 244
    emit("---------------------------------------------------------------------------------------");
    emit("-- Title          : Wishbone slave core for "..periph.name);
    emit("---------------------------------------------------------------------------------------");
245
    emit("-- File           : "..file_name);
twlostow's avatar
twlostow committed
246 247
    emit("-- Author         : auto-generated by wbgen2 from "..input_wb_file);
    emit("-- Created        : "..os.date());
248 249 250
		if (periph.version ~= nil) then
			 emit("-- Version        : "..string.format('0x%08X', periph.version));
		end
twlostow's avatar
twlostow committed
251 252 253 254 255 256 257 258 259 260 261 262
    emit("-- Standard       : VHDL'87");
    emit("---------------------------------------------------------------------------------------");
    emit("-- THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE "..input_wb_file);
    emit("-- DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!");
    emit("---------------------------------------------------------------------------------------");
		emit("");
		emit("library ieee;");
		emit("use ieee.std_logic_1164.all;");
		emit("use ieee.numeric_std.all;");

-- do we have RAMs or FIFOs? - if yes, include the wbgen2 components library.
		if(periph.ramcount > 0 or periph.fifocount > 0 or periph.irqcount > 0) then
263 264
--			emit("library wbgen2;");
			emit("use work.wbgen2_pkg.all;");
twlostow's avatar
twlostow committed
265 266
		end

267 268 269 270
        if(options.hdl_reg_style == "record_full") then
    		emit("use work.wishbone_pkg.all;");
        end

twlostow's avatar
twlostow committed
271 272 273
		emit("");
end

274 275
function cgen_vhdl_interface_declaration(keyword)
  emit (keyword.." "..periph.hdl_entity.." is");
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
  indent_right();

  if(table.getn(g_optlist) ~= 0) then
     emit ("generic (");
     indent_right();
     emiti()

     for i,v in pairs(g_optlist) do
        emiti();
        emitx(v.." : integer := 1");
        if(i ~= table.getn(g_optlist)) then
           emit(";")
        else
           emit(");")
        end
     end
     indent_left();
  end

  indent_left();

twlostow's avatar
twlostow committed
297 298 299 300 301 302 303 304
	indent_right();
  emit ("port (");
	indent_right();

-- emit the ports.
  for i=1,table.getn(g_portlist) do
		local port = g_portlist[i];

305 306 307 308 309 310 311 312 313
        local generate = true;

        if( options.hdl_reg_style == "record" and port.is_reg_port ) then
          generate = false;
        elseif ( options.hdl_reg_style == "record_full" and (port.is_reg_port or port.is_wb) ) then
          generate = false;
        end

       if(generate) then
twlostow's avatar
twlostow committed
314

315 316 317 318 319
       -- if we have a comment associated with current port, let's emit it before the port definition.
       if(port.comment ~= nil and port.comment ~= "") then
          emitx("-- "..port.comment.."\n");
       end
       
320
--       print(port.name.." "..port.type)
321 322
       -- generate code for the port
       local line = string.format("%-40s : %-6s %s", port.name, port.dir, fieldtype_2_vhdl[port.type]);
twlostow's avatar
twlostow committed
323

Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
324
       if(port.range > 1 or port.type == SLV) then
325 326
          line = line.."("..(port.range-1).." downto 0)";
       end    
twlostow's avatar
twlostow committed
327

328
       -- eventually append a semicolon
329
       line=line..csel((i == table.getn(g_portlist)) and not (options.hdl_reg_style == "record" or options.hdl_reg_style == "record_full"), "", ";");
twlostow's avatar
twlostow committed
330

331 332 333
       -- and spit out the line
       emit(line);
    end
twlostow's avatar
twlostow committed
334 335
  end

336 337 338 339 340 341 342 343 344 345
  if(options.hdl_reg_style == "record_full") then
     emit(string.format("%-40s : %-6s %s;", "slave_i", "in", "t_wishbone_slave_in"));
     emit(string.format("%-40s : %-6s %s;", "slave_o", "out", "t_wishbone_slave_out"));
     emit(string.format("%-40s : %-6s %s;", "int_o", "out", "std_logic"));

  end

  if(options.hdl_reg_style == "record" or options.hdl_reg_style == "record_full") then
     emit(string.format("%-40s : %-6s %s;", "regs_i", "in",  "t_"..periph.hdl_prefix.."_in_registers"));
     emit(string.format("%-40s : %-6s %s",  "regs_o", "out", "t_"..periph.hdl_prefix.."_out_registers"));
346
  end
347

twlostow's avatar
twlostow committed
348 349 350 351
    
	indent_left();
 	emit(");");
	indent_left();
352 353 354 355 356 357

    if ( keyword == "component") then
    	emit("end component;");
    else
	  emit("end "..periph.hdl_entity..";");
    end
twlostow's avatar
twlostow committed
358 359
	emit("");

360 361 362 363 364 365 366 367 368 369 370 371 372

end

-- function generates VHDL entity header (ports and generics) and beginning of ARCHITECTURE block (signal and constant definitions).
function cgen_vhdl_entity()
	local last;

   if(options.hdl_reg_style == "record" or options.hdl_reg_style == "record_full") then
      emit("use work."..get_pkg_name()..".all;");
      emit("\n");
   end

   cgen_vhdl_interface_declaration("entity")
twlostow's avatar
twlostow committed
373 374 375 376 377 378 379 380
-- generate the ARCHITECTURE block with signal definitions

	emit("architecture syn of "..periph.hdl_entity.." is");
	emit("");

-- we do it the same way as for the ports. 
	for i,v in pairs (g_siglist) do
		s=string.format("signal %-40s : %-15s", v.name, fieldtype_2_vhdl[v.type]);
twlostow's avatar
twlostow committed
381
		if(v.range > 0 and v.type ~= BIT) then
twlostow's avatar
twlostow committed
382 383 384 385 386
			s=s..string.format("(%d downto 0)", v.range-1);
		end
		s=s..";";
		emit(s);
	end
387

twlostow's avatar
twlostow committed
388 389 390 391
	emit("");

	emit("begin");
	indent_right();
392

393 394 395
--   if(options.hdl_reg_style == "record") then
--      emit("regs_b <= c_"..periph.hdl_prefix.."_registers_init_value;");
--   end
twlostow's avatar
twlostow committed
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
end

-- function generates the ending of VHDL file - in our case an END statement, closing the single ARCHITECTURE block.
function cgen_vhdl_ending()
	indent_left();
	emit("end syn;");
end

-- This is the main code generator function. It takes a syntax tree [tree], and traverses it recursively, producing VHDL code.
-- Note that it supports only a small subset of VHDL language which is used by slave cores. All the following functions are private.
function cgen_generate_vhdl_code(tree)

	-- function searches for a subnode of type [t] in node [node].
	function find_code(node, t)
		for i,v in ipairs(node) do if ((v.t ~= nil) and (v.t == t)) then return v; end end
		return nil;
	end

	-- function generates a synchronous process, e.g. a process which looks like:
	-- process (clk, rst)
	-- if(rst)
	-- [put code from vreset subnode here]
	-- elsif rising_edge(clk)
	-- [put code from vposedge subnode here]
	-- end 

	function cgen_vhdl_syncprocess(node)
		emit("process ("..node.clk..", "..node.rst..")");
		emit("begin");
		indent_right();

	-- search for reset and posedge subnodes. 			
		local vrst = find_code(node.code, "reset");
		local vpe = find_code(node.code, "posedge");

	-- no posedge block - then what our process is supposed to be doing? :D			
		if(vpe == nil) then die("vhdl code generation error: no vposedge defined for vsyncprocess"); end
			
	-- generate the process body depending on the type of reset (sync/async)
		if(options.reset_type == "asynchronous") then
			if(vrst ~= nil) then
				emit("if ("..node.rst.." = '"..vrst.level.."') then ");
				indent_right();

	-- recursively emit the vreset subnode
				recurse(vrst.code);
				indent_left();
	
				emit("elsif rising_edge("..node.clk..") then");
				indent_right();
		
			else
				emit("if rising_edge("..node.clk..") then");			
				indent_right();
			end

	-- -- recursively emit the vposedge
			recurse(vpe.code);		
			indent_left();
	 	  emit("end if;");
			
		else -- the same as above, but with synchronous reset
			emit("if rising_edge("..node.clk..") then");
			indent_right();
			
			if(vrst ~= nil) then
				 emit("if ("..node.rst.." = '"..vrst.level.."') then ");
				 indent_right();
				 recurse(vrst.code);
				 indent_left();
				 emit("else ");
			end
	 		  
			indent_right();
			recurse(vpe.code); 		  
			indent_left();
			emit("end if;");
			indent_left();
			emit("end if;");
		end
		indent_left();

		emit("end process;");	
		emit("");
		emit("");
	end

-- emits a VHDL combinatorial process
	function cgen_vhdl_combprocess(node)
		local first_one = true;
		emiti();
		emitx("process (");

		for i,v in pairs(node.slist) do
			if(first_one) then
				first_one = false;
			else
				emitx(", ");
			end
			emitx(v);
		end
		
		emit(")");
		emit("begin");

		indent_right();
		recurse(node.code);
		indent_left();

		emit("end process;");	
		emit("");
		emit("");
	end


	-- function takes a node and determines it's type, value and range 
	function node_typesize(node)
		local ts = {};
		local sig;

--		print("tsize",node);

		ts.node = node;

	-- if it's a direct signal or a numeric constant, it simply returns it.

	-- our node is a LUA table, which most likely means that it's a non-terminal leaf of syntax tree.
		if(type(node) == "table") then

	-- if the subnode is of type vi (indexed signal)
			if(node.t ~= nil and node.t == "index") then

	-- find the definition of the signal to determine its VHDL type.
				sig = cgen_find_sigport(node.name);

	-- and extract indexing bondaries (h downto l)
				ts.h=node.h;
				ts.l=node.l;
				ts.name=sig.name;
				ts.type=sig.type;
				
					if(ts.l == nil) then
						ts.size = 1;
						ts.type = BIT;
					else
						ts.size = ts.h-ts.l+1;
					end
			
				return ts;
			elseif(node.t ~= nil and node.t == "undefined") then
				ts.type = UNDEFINED;
				return ts;

	-- if the subnode is not of "vi" type, treat is as an expression (arithmetic, logic, etc.)
		 	else
					ts.type = EXPRESSION;
					ts.code = node;
					return ts;
			end

	-- node is a LUA string - it's a signal or port. Determine its type, range and return it to the caller.
		 elseif(type(node) == "string") then
			sig = cgen_find_sigport(node);
			ts.size = sig.range;
			ts.type = sig.type;
			ts.name = node;
			return ts;

	-- node is a LUA number - just return it, the caller should take care of range/type determination depending on the assignment target.
		 elseif(type(node) == "number") then
			ts.type = INTEGER;
			ts.name = node;
			ts.size = 0;
			return ts;
		else 
			die("vhdl cgen internal error: node_typesize got an unknown node.");
		end
	end

	-- generates the signal name with subrange (if applies): signame, signame(h downto l) or signame(h). 
	function gen_subrange(t)
577
     local n = port2record(t.name);
twlostow's avatar
twlostow committed
578
		-- node is a VHDL "open" pin declaration?
579 580 581 582

     if(type(t.node) == "table" and t.node.t == "openpin") then
        return "open";
     end
twlostow's avatar
twlostow committed
583

twlostow's avatar
twlostow committed
584 585 586
		--print("gensub: ", t.name);

		if (t.h ~= nil and ( t.l == nil or (t.l == t.h))) then
587
			return n.."("..t.h..")";
twlostow's avatar
twlostow committed
588
		elseif(t.h ~= nil and t.l ~= nil) then
589
			return n.."("..t.h.." downto "..t.l..")";
twlostow's avatar
twlostow committed
590
		else
591
			return n;
twlostow's avatar
twlostow committed
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
		end
	end

	-- calculates bit-size of a signal
	function calc_size(t)
		if(t.h ~= nil and t.l == nil) then
			return 1;
		elseif(t.h ~= nil and t.l ~= nil) then
			return t.h-t.l+1; 
		else
			local sig= cgen_find_sigport(t.name);
			return sig.range;
		end
	end

	-- WARNING! UGLY CODE!
	-- generates a VHDL type-conversion code for assignment between tsd (destination node) and tss (source node).
	function gen_vhdl_typecvt(tsd, tss)

twlostow's avatar
twlostow committed
611 612 613
	-- print("Gen_typecvt:",  tsd.name);
	-- print("Gen_typecvt:",  tss.name, tss.type);

twlostow's avatar
twlostow committed
614 615 616 617 618 619 620 621 622 623 624 625
	-- types match? Coool, we have nothing to do
		if(tsd.type == tss.type) then 
			return(gen_subrange(tss));

		elseif (tss.type == UNDEFINED) then
			return "'X'"
	-- dest: bit/slv/signed/unsigned <= src: numeric_constant;
		elseif (tss.type == INTEGER) then

			if(tsd.type == BIT) then 
				return("'"..tss.name.."'");
			elseif(tsd.type == SLV) then
626 627
--				return("std_logic_vector(to_unsigned("..tss.name..", "..calc_size(tsd).."))");
					return gen_vhdl_bin_literal(tss.name, calc_size(tsd));
twlostow's avatar
twlostow committed
628 629 630 631 632 633 634 635 636 637
			elseif(tsd.type == SIGNED) then
				return("to_signed("..tss.name..", "..calc_size(tsd)..")");
			elseif(tsd.type == UNSIGNED) then
				return("to_unsigned("..tss.name..", "..calc_size(tsd)..")");
			else die ("unsupported assignment: "..tsd.name.." "..tss.name); end	

	-- dest: bit <= src: SLV
		elseif (tss.type == BIT) then

			if(tsd.type == SLV) then
twlostow's avatar
twlostow committed
638
				return(gen_subrange(tss));
twlostow's avatar
twlostow committed
639 640 641 642 643 644 645
			else die ("unsupported assignment: "..tsd.name.." "..tss.name); end	

		elseif (tss.type == SIGNED or tss.type == UNSIGNED) then

	-- dest: slv <= src: signed/unsigned
			if(tsd.type == SLV) then
				return("std_logic_vector("..gen_subrange(tss)..")");
twlostow's avatar
twlostow committed
646
		 else die ("unsupported assignment: "..tsd.name.." "..tss.name); end	
twlostow's avatar
twlostow committed
647 648 649 650 651 652 653 654 655

		elseif (tss.type == SLV) then

	-- dest: signed/unsigned <= src: slv
			if(tsd.type == SIGNED) then
				return("signed("..gen_subrange(tss)..")");
			elseif (tsd.type == UNSIGNED) then
--					print(tss);
				return("unsigned("..gen_subrange(tss)..")");
twlostow's avatar
twlostow committed
656 657 658 659 660
		 elseif (tsd.type == BIT) then
				return gen_subrange(tss);
		 else 
				die ("unsupported assignment: "..tsd.name.." "..tss.name); end	
		 
twlostow's avatar
twlostow committed
661 662 663 664 665 666 667 668 669
		else die ("unsupported assignment: "..tsd.name.." "..tss.name); end	
	end

	-- function generates VHDL code for assignment-type node (node.src <= node.dst).
	function cgen_vhdl_assign(node)
	-- determine types and ranges of source and destination node.
		local tsd = node_typesize(node.dst);
		local tss = node_typesize(node.src);

twlostow's avatar
twlostow committed
670 671
	--	print(tsd.name);

twlostow's avatar
twlostow committed
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
	-- source node is an expression? - recurse it
		if(tss.type == EXPRESSION) then
			emiti();
		--	print(gen_subrange(tsd));
			emitx(gen_subrange(tsd).." <= ");
			recurse({tss.code});
			emitx(";\n");
		else
	-- not an expression? - assign the destination with proper type casting.
			emit(gen_subrange(tsd).." <= "..gen_vhdl_typecvt(tsd, tss)..";");
		end
	end

	-- function generates an if..else..end if control block.
	function cgen_vhdl_if(node)

		emiti(); emitx("if (");
	-- recurse the condition block
		recurse(node.cond);
		emitx(") then\n");

	-- "if..else" construct
		if(node.code_else ~= nil) then
			indent_right();		recurse(node.code);		indent_left();
			emit("else");
			indent_right();		recurse(node.code_else);		indent_left();
			emit("end if;");
	-- just "if" construct
		else
			indent_right();	recurse(node.code);	indent_left();
			emit("end if;");
		end
	end

706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
	-- function generates an if..else..end if control block.
	function cgen_vhdl_generate_if(node)
     if(g_gen_block_count == nil) then
        g_gen_block_count = 0
     else
        g_gen_block_count = g_gen_block_count + 1
     end
     
     gname = string.format("genblock_%d", g_gen_block_count)
		emiti(); emitx(gname..": if (");
	-- recurse the condition block
		recurse(node.cond);
		emitx(") generate\n");

    indent_right();	recurse(node.code);	indent_left();
    emit("end generate "..gname..";");
 end


twlostow's avatar
twlostow committed
725 726 727 728 729 730 731 732 733 734 735 736 737
	-- function generates a NOT unary expression.
	function cgen_vhdl_not(node)
	-- check type of node to be NOTed
		local tsa = node_typesize(node.a);

		emitx("not ");

	-- recurse
		if(tsa.type == EXPRESSION) then
			emitx("("); recurse({node.a}); emitx(")");
		else
	-- or emit the value/signal
			emitx(gen_subrange(tsa));
738
	 end
twlostow's avatar
twlostow committed
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
	end


	-- function generates code for a VHDL binary expression.
	function cgen_vhdl_binary_op(node)


		local tsa = node_typesize(node.a);
		local tsb = node_typesize(node.b);
		local op=node.t;


	-- emit the left-side operand
		if(tsa.type == EXPRESSION) then
			emitx("("); recurse({node.a}); emitx(")");
		else
			emitx(gen_subrange(tsa));
		end

	-- emit the operator
		if(op=="eq") then emitx(" = "); end
		if(op=="and") then emitx(" and "); end
		if(op=="or") then emitx(" or "); end
		if(op=="sub") then emitx(" - "); end
		if(op=="add") then emitx(" + "); end

	-- ..and the right-side operand
		if(tsb.type == EXPRESSION) then
			emitx("("); recurse({node.b}); emitx(")");
		else
			emitx(gen_vhdl_typecvt(tsa, tsb));
		end
	end


	-- 
	function cgen_vhdl_comment(node)
twlostow's avatar
twlostow committed
776
--	print("COMMENT: "..node.str);
twlostow's avatar
twlostow committed
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898
		emitx("-- "..node.str.."\n");
	end


	-- function generates VHDL switch/case statement
	function cgen_vhdl_switch(node)

		local tsa = node_typesize(node.a);

		emiti(); emitx("case ");

	-- recurse the case header: case (expression) is
		if(tsa.type == EXPRESSION) then
			emitx("("); recurse({node.a}); emitx(")");
		else
			local tsb = {};
			tsb.type = SLV;
			emitx(gen_vhdl_typecvt(tsb, tsa));
		end	

		emitx(" is\n");

	-- iterate all the subnodes
		for i,v in pairs(node.code)  do

	-- it's a case match:
			if(v.t == "case") then			
				emit("when "..gen_vhdl_bin_literal(v.a, tsa.size).." => ");
				indent_right();
				recurse({v.code});
				indent_left();

	-- it's a default match: (when others =>)
			elseif(v.t == "casedefault") then			
				emit("when others =>");
				indent_right();
				recurse({v.code});
				indent_left();
			end	
		end

		emit("end case;");
	end


	-- function instantiates and wires a VHDL component.
	function cgen_vhdl_instance(node)
		local num_pmaps=0;
		local num_gmaps=0;
		local n;

	-- emit the instatiation code
		emit(node.name.." : "..node.component);

	-- count the number of PORT MAPs and GENERIC MAPs
		for i,v in pairs(node.maps) do
			if(v.t=="genmap") then
				num_gmaps=num_gmaps+1;
			elseif(v.t == "portmap") then
				num_pmaps=num_pmaps+1;
			end 
		end
	
	-- do we gave GENERIC MAPs?	
		if(num_gmaps > 0) then
			indent_right();
			emit("generic map (");
			indent_right();
			n=1;

	-- then emit all of them
			for i,v in pairs(node.maps) do
				if(v.t=="genmap") then
				emit(string.format("%-20s => %s", v.to, v.from)..csel(n==num_gmaps,"",","));
				n=n+1;
				end
			end	
			indent_left();
			emit(")");
			indent_left();		
		end	

	-- do we have PORT MAPs?
		if(num_pmaps > 0) then
			indent_right();
			emit("port map (");
			indent_right();
			n=1;
			for i,v in pairs(node.maps) do
				if(v.t=="portmap") then
				local tsd = node_typesize(v.from);
				emit(string.format("%-20s => %s", v.to, gen_subrange(tsd))..csel(n==num_pmaps,"",","));
				n=n+1;
				end
			end	
			indent_left();
			emit(");");
			indent_left();		
		end	

		emit("");
	end

	-- generates VHDL "others => value" construct
	function cgen_vhdl_others(node)
		emitx("(others => '"..node.val.."')");
	end
	
	-- generates VHDL "pin => open" mapping
	function cgen_vhdl_openpin(node)
		emitx("open");
	end
	
	-- the main recursive traversal function.
	function recurse(node)

		local generators = {
			["comment"] 			= cgen_vhdl_comment;
			["syncprocess"] 	= cgen_vhdl_syncprocess;
			["combprocess"] 	= cgen_vhdl_combprocess;
			["assign"]			 	= cgen_vhdl_assign;
			["if"]			 			= cgen_vhdl_if;
899
			["generate_if"]		= cgen_vhdl_generate_if;
twlostow's avatar
twlostow committed
900 901 902 903 904 905 906 907 908 909 910 911
			["eq"]					 	= cgen_vhdl_binary_op;
			["add"]					 	= cgen_vhdl_binary_op;		
			["sub"]					 	= cgen_vhdl_binary_op;
			["or"]					 	= cgen_vhdl_binary_op;
			["and"]					 	= cgen_vhdl_binary_op;
			["not"]						= cgen_vhdl_not;
			["switch"] 				= cgen_vhdl_switch;
			["instance"]			= cgen_vhdl_instance;
			["others"]				= cgen_vhdl_others;
 			["openpin"]				= cgen_vhdl_openpin;
		};

twlostow's avatar
twlostow committed
912
	--	print(node);
twlostow's avatar
twlostow committed
913 914 915 916 917 918 919 920

		for i,v in pairs(node) do
			-- no type? probably just a block of code. recurse it deeper.

			if(v.t == nil) then
				recurse(v);
			else
				local func = generators[v.t];
921
	--		print(v.t);
twlostow's avatar
twlostow committed
922 923 924 925 926 927
				if(func == nil) then
					die("Unimplemented generator: "..v.t);
				end
				func(v);
			end
		end
928 929
 end

930
  if((options.hdl_reg_style == "record" or options.hdl_reg_style == "record_full") and options.output_package_file ~= nil) then
931 932 933 934 935 936 937 938
     cgen_generate_init(options.output_package_file);
     cgen_new_snippet();
     cgen_vhdl_header(options.output_package_file);
     cgen_vhdl_package();
     cgen_write_current_snippet();
     cgen_generate_done();
  end

twlostow's avatar
twlostow committed
939

940
	cgen_generate_init(options.output_hdl_file)
twlostow's avatar
twlostow committed
941 942 943
-- here we go. Let's create a new snippet of code. VHDL generator is single-pass, so we'll need only one snippet.
	cgen_new_snippet();
-- output the header,
944 945
  
	cgen_vhdl_header(options.output_hdl_file);
twlostow's avatar
twlostow committed
946 947 948 949 950 951 952 953 954 955
-- .. the entity declaration
	cgen_vhdl_entity();
-- the main code
	recurse(tree);
-- the ending
	cgen_vhdl_ending();

-- and flush the snippet to the output file :)
	cgen_write_current_snippet();

956
	cgen_generate_done();
twlostow's avatar
twlostow committed
957 958 959
-- voila, we're done!
end