HDL4SE:软件工程师学习Verilog语言(八)

笔记 专栏收录该内容
25 篇文章 0 订阅

8 用c语言写自定义基本单元

本节本来是要继续学习verilog语言的,但是软件开发进度条太沉重了,怎么推也推不动,只能慢慢磨了。没有软件的配合,学习效果就差了一些,因此把后面的内容提到前面来。当然也是因为沉迷于编程及游戏,资料都没有心思写了。
本节还是通过一个例子,来说明如何在HDL4SE中用c语言写自己的基本单元库,然后让verilog来调用。基本的想法是,用c语言写一个LCOM对象,实现基本单元所需要的IHDL4SEUnit, IHDL4SEDetector等接口,当然顺便实现IDList接口以便它能加入到双向链表中。用verilog描述它的模型,然后就可以包含在其他的verilog代码中通过实例化使用了。
这种基本单元可大可小,可以是一个小的运算单元,比如一个深度神经网络中用的基本乘累加算子,可以是一个比较大的单元,比如干脆就是一个RISC-V的核。我们这个例子是要实现一个俄罗斯方块游戏,基本单元就是游戏机的控制器。当然游戏机的显示器也可以看成是一个基本单元,作为设备挂在HDL4SE模拟器上,其实也是一个基本单元,只是没有用verilog代码直接调用罢了。此时verilog代码就很简单了,就是把控制器与模拟环境连接在一起,具体方式看后面的说明。
你可能会觉得有点投机取巧,用C语言来写个控制器,然后游戏就跑起来了,其中verilog代码几乎就没有了,仅仅是把游戏控制器和游戏面板连接起来。然而这正是做HDL4SE项目的初衷。HDL4SE可以支持用C语言来写数字电路基本单元模型,有几个理由:

  1. 在系统设计初期,很多模块都没有确定如何设计,此时就写verilog代码是不合适的,但是用c语言写一个原型系统,相对就轻松多了。因此才开始可以用c语言参与搭建系统,可以视为CModel。毕竟写c还是要比写verilog 的RTL描述要容易很多。
  2. 随着系统的概要设计迭代次数增加,逐步稳定,一部分模块可以用verilog来写了,此时将c语言模型和verilog编写的电路联合在一起模拟,可以检验verilog实现的正确性,而且能极大地加快仿真速度。有些部分甚至只是辅助开发的模块,其实最终都不需要写成RTL代码。
  3. 很多不适于用RTL编写的测试输入可以用c语言来写,这样可以简化系统复杂度,比如想做个深度神经网络识别的verilog代码,要从JPG文件中直接读图片数据,就可以用c语言写一个JPEG文件解码模块挂在系统中模拟,否则真用verilog RTL来写,好难啊,这里就不堪细说了,做过的都知道。即使写出来了,这个模块运行起来也快不起来,占用了很多仿真时间。一般FPGA开发或ASIC开发项目只好用软件将JPEG文件解码后转换成verilog能直接读的文本文件,然后用verilog代码来读,其实已经很不方便了。
  4. 可以在仿真过程中增加一些交互的内容,比如做GPU开发,可以马上看到绘图结果,做信号处理,直接看到信号处理结果(比如直接挂个FFT分析模块在系统中,并能动态显示频谱分布),做游戏,至少要能玩起来。这个比用纯的verilog RTL编玩后仿真,然后再去分析输出信号波形图或者输出文件,要爽很多。当然直接拉低了做数字电路那帮人的逼格。软件工程师从此包打天下,数字电路?软件开发而已。
  5. 可以将设备驱动程序软件和硬件一起模拟,让驱动程序软件在早期就介入系统设计,减少软硬设计之间的摩擦,这个就不多说了,做过类似项目的都知道其中的好处。
    我们还是从一个俄罗斯方块游戏例子开始,然后介绍其中的基本单元库的c语言实现,最后用verilog代码把它们在HDL4SE模拟器中连接起来,构成一个实际能玩的游戏。后面会随着学习深入,编译器软件的完善,逐步将c语言实现的部分替换成verilog实现,最终形成一个用verilog写的俄罗斯方块游戏。烧在FPGA里,可以做成一个游戏机呢,做成专用的ASIC芯片,可以用来造游戏机。

8.1 俄罗斯方块游戏

俄罗斯方块游戏看上去是这个样子的:
在这里插入图片描述
当然你可能玩过的有些不同,不过基本上还是一样的,左边是游戏的显示面板,一组形状不同的方块从上面不断掉落下来,其中黑色的是空的,带颜色的是实心的,如果它落到下面有一个实心的块被面板中已经有的实心块或者面板的底部挡住,此时这组方块就变成面板的一部分,你可以通过左右键控制方块组左右运动,也可以按旋转键(我们的实现用向上键来实现)来旋转方块,当然如果位置已经调整的不错了,可以用向下键加速方块组下落。面板中的方块如果在水平上构成一个全部是实心方块的行,这一行就会消失,面板中上面的方块都往下移动一行,空出来的地方又可以放方块了。当然由于一组方块最多是4x4的,因此有可能一次最多有四行都变成实心行,同时消失。如果落下来的块没地方放了,那就GAME OVER,我们这里选择自动重新开始。
右边的上方是后面会掉落下一个方块组的结构。下面有三行字,L开始的一行代表总共有多少个实心行消失,P开始的一行表示目前的速度,我们的系统中速度从0–6000,C开始的一行表示得分,我们的计分规则是:
开局200分,左右移动方块组扣一分,旋转方块组扣一分,向下加速一次加两分,消除一个单行加10分,一次消除两行得40分,一次消除三行得160分,一次消除4行得640分。当前的速度就是得分除以10。得分越高,速度越快。得分为零就从头开始,得分超出显示范围就通关。
讲了这么多可能都烦了,其实游戏还是很简单的。假如你想做这么一个游戏的话,其实分两个部分,一个是游戏的面板,一个是游戏的控制模块。游戏面板要能动态显示游戏的内容,同时也接收按键信息送到控制代码,控制模块则来控制游戏的进程,并控制显示的内容。

8.2 显示面板

显示面板提供一个类似于帧存的可写地址区域,我们的显示面板有16x24个方块位置,一个4x4的下一块形状,三个整数表示消除行数,速度和得分。每个方块有16种颜色,因此每个方块用4位表示,三个整数各用一个32位整数表示。这样,显示面板的访问地址定义如下(偏移地址):

偏移地址说明
0x00只读,表示键盘状态,低四位有效,分别表示键盘上的右,左,下,上四个键的状态,
1表示键按下,0表示没有按下。
0x10–0xcf每个32位表示8个方块的颜色,总共有16x24个方块,这样占1536位,192个8位字节地址。
0xe0,0xe4总共8个字节,64位,存储下一块组(4x4个块)的颜色。
0xf0得分
0xf4消除的行数
0xf8速度

按照HDL4SE的模拟器设计,显示面板作为设备挂在模拟器中,跟一般的verilog模块一样,应实现LCOM接口IHDL4SEUnit,可选实现IHDL4SEDetector。
下面就是LCOM的八股文了,软件工程师都比较自由,每个人都想写出自己风格的代码,这在软件工程中简直就是一个灾难,为此LCOM规定了编写代码的规范,包括函数命名,要实现的接口函数的名称,参数名称和类型,返回值,实现一个接口所需要做的若干事务等等,确保每个人实现后能够连接在一起,并且可以相互看得懂代码结构。这里俗称LCOM八股文。

8.2.1 LCOM八股 – 起

定义接口IID和接口,一个类CLSID,显示面板对象没有定义新的接口,因此只有一个CLSID,为方便使用,还定义了一个快速对象生成的函数,当然还定义了面板的大小,24行,每行16个方块。这部分是给外部模块调用的,因此比较规范,只是说明要做个什么对象。

DEFINE_GUID(CLSID_TERRISDEVICE, 0x81de7969, 0xf783, 0x4023, 0xbd, 0xe6, 0xf8, 0x15, 0xf8, 0xd5, 0x9c, 0x5);
DEFINE_GUID(PARAMID_BASEADDR,0x55faed75, 0xefbe, 0x461d, 0x81, 0x99, 0x90, 0x58, 0x88, 0xab, 0x47, 0x93);

IHDL4SEUnit** guiCreate(unsigned int baseaddr, const char* name);

#define XCOUNT  16
#define YCOUNT  24

8.2.2 LCOM八股 – 承

然后定义类实现需要的数据结构以及相关接口的实现定义,把接口定义的函数和数据都声明出来,大部分是LCOM八股的规定动作。这部分描述需要实现的接口,当然也定义一些实现相关的数据,给后面的转留下一些伏笔,算是承上启下的部分吧:

#define WIDTH  600
#define HEIGHT 800

/*
terris panel & keyboard
*/
typedef struct _sTerrisDevice {
	OBJECT_HEADER
	INTERFACE_DECLARE(IHDL4SEUnit)
	HDL4SEUNIT_VARDECLARE
	INTERFACE_DECLARE(IHDL4SEDetector)
	HDL4SEDETECTOR_VARDECLARE
	DLIST_VARDECLARE
		
	IHDL4SEModule** parent;
	char* name;
	GLFWwindow* window;
	int width;
	int height;
	int count;
	unsigned int baseaddr;
	unsigned int panelvalue[YCOUNT][XCOUNT]; /* 00000010--000000cc*/
	unsigned int nextblock[4][4];           /* 000000e0, 000000e4*/
	unsigned int score;                      /* 000000f0*/
	unsigned int level;                      /* 000000f4*/
	unsigned int speed;                      /* 000000f8*/
	
	unsigned int keypressed;                 /* 00000000 */

	unsigned int portdata[9];

	int wRead;              /* 上周期的读信号 */
	unsigned int bReadAddr; /* 决定当前周期是否响应 */
	int wRead_cur;
	unsigned int bReadAddr_cur;
	 
	/*
	从设备的8个输入端口
		0.wClk
		1.nwReset
		2.wWrite
		3.bWriteAddr
		4.bWriteData
		5.bWriteMask
		6.wRead
		7.bReadAddr
		8.bReadData
	*/
	IHDL4SEUnit** fromunit[9];
	int           fromindex[9];

}sTerrisDevice;

OBJECT_FUNCDECLARE(terrisdevice, CLSID_TERRISDEVICE);
HDL4SEUNIT_FUNCDECLARE(terrisdevice, CLSID_TERRISDEVICE, sTerrisDevice);
HDL4SEDETECTOR_FUNCDECLARE(terrisdevice, CLSID_TERRISDEVICE, sTerrisDevice);
DLIST_FUNCIMPL(terrisdevice, CLSID_TERRISDEVICE, sTerrisDevice);

OBJECT_FUNCIMPL(terrisdevice, sTerrisDevice, CLSID_TERRISDEVICE);


QUERYINTERFACE_BEGIN(terrisdevice, CLSID_TERRISDEVICE)
QUERYINTERFACE_ITEM(IID_HDL4SEUNIT, IHDL4SEUnit, sTerrisDevice)
QUERYINTERFACE_ITEM(IID_HDL4SEDETECTOR, IHDL4SEDetector, sTerrisDevice)
QUERYINTERFACE_ITEM(IID_DLIST, IDList, sTerrisDevice)
QUERYINTERFACE_END

8.2.3 LCOM八股 – 转

这部分是八股文的核心,开题后,就要实现其中的各个功能,转的核心要点就是要按照开题的要求,逐个去实现每个接口的函数。这部分包括两个小部分,一个是实现LCOM对象的一些通用函数,一转:

static const char* terrisdeviceModuleInfo()
{
	return "1.0.0-20210610.0829 Terris Panel";
}

static int keypressed = 0;

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
	keypressed = 0;
	if (action != GLFW_PRESS)
		return;
	if (key >= GLFW_KEY_RIGHT && key <= GLFW_KEY_UP) {
		key -= GLFW_KEY_RIGHT;
		keypressed = (1 << key);
	}
}

static int terrisdeviceCreate(const PARAMITEM* pParams, int paramcount, HOBJECT* pObject)
{
	sTerrisDevice* pobj;
	int i;
	pobj = (sTerrisDevice*)malloc(sizeof(sTerrisDevice));
	if (pobj == NULL)
		return -1;
	*pObject = 0;
	HDL4SEUNIT_VARINIT(pobj, CLSID_TERRISDEVICE);
	INTERFACE_INIT(IHDL4SEUnit, pobj, terrisdevice, hdl4se_unit);
	INTERFACE_INIT(IHDL4SEDetector, pobj, terrisdevice, hdl4se_detector);
	DLIST_VARINIT(pobj, terrisdevice);

	pobj->baseaddr = 0xF0000000;
	pobj->name = NULL;
	pobj->parent = NULL;

	for (i = 0; i < paramcount; i++) {
		if (pParams[i].name == PARAMID_HDL4SE_UNIT_NAME) {
			if (pobj->name != NULL)
				free(pobj->name);
			pobj->name = strdup(pParams[i].pvalue);
		}
		else if (pParams[i].name == PARAMID_BASEADDR) {
			pobj->baseaddr = pParams[i].i32value;
		}
	}

	for (i = 0; i < 9; i++) {
		pobj->fromunit[i] = NULL;
	}

	memset(pobj->panelvalue, 0, sizeof(pobj->panelvalue));
	pobj->panelvalue[12][7] = 4;
	pobj->panelvalue[12][8] = 9;
	pobj->panelvalue[12][9] = 3;
	pobj->panelvalue[11][9] = 13;

	if (!glfwInit())
		return -1;
	pobj->width = WIDTH;
	pobj->height = HEIGHT;
	pobj->wRead = 0;
	pobj->count = 0;
	pobj->wRead_cur = 0;
	pobj->window = glfwCreateWindow(WIDTH, HEIGHT, "TERRIS", NULL, NULL);
	if (!pobj->window)
	{
		glfwTerminate();
		return -2;
	}
	pobj->keypressed = 0;

	glfwSetWindowAspectRatio(pobj->window, WIDTH, HEIGHT);
	
	glfwSetKeyCallback(pobj->window, key_callback);
	glfwMakeContextCurrent(pobj->window);
	gladLoadGL(glfwGetProcAddress);
	glfwSwapInterval(1);

	glfwGetFramebufferSize(pobj->window, &pobj->width, &pobj->height);

	/* 返回生成的对象 */
	OBJECT_RETURN_GEN(terrisdevice, pobj, pObject, CLSID_TERRISDEVICE);
	return EIID_OK;
}

static void terrisdeviceDestroy(HOBJECT object)
{
	sTerrisDevice* pobj;
	int i;
	pobj = (sTerrisDevice*)objectThis(object);
	if (pobj->name != NULL)
		free(pobj->name);
	for (i = 0; i < 9; i++) {
		objectRelease(pobj->fromunit[i]);
	}
	glfwTerminate();
	memset(pobj, 0, sizeof(sTerrisDevice));
	free(pobj);
}

static int terrisdeviceValid(HOBJECT object)
{
	sTerrisDevice* pobj;
	pobj = (sTerrisDevice*)objectThis(object);
	return 1;
}

第二部分是每个接口函数的实现,再转:

static int terrisdevice_hdl4se_unit_Connect(HOBJECT object, int index, HOBJECT from, int fromindex)
{
	sTerrisDevice* pobj;
	IHDL4SEUnit** unit = NULL;
	pobj = (sTerrisDevice*)objectThis(object);
	if (index >= 0 && index < 9) {
		if (0 == objectQueryInterface(from, IID_HDL4SEUNIT, (void**)&unit)) {
			pobj->fromunit[index] = unit;
			pobj->fromindex[index] = fromindex;
			return 0;
		}
		else {
			return -2;
		}
	}
	return -1;
}

static int terrisdevice_hdl4se_unit_ConnectPart(HOBJECT object, int index, int start, int width, HOBJECT from, int fromindex)
{
	/* 不支持部分连接 */
	return 0;
}

#define isMyAddr(addr) ((addr & 0xFFFFFF00) == (pobj->baseaddr & 0xFFFFFF00))

static int terrisdevice_hdl4se_unit_GetValue(HOBJECT object, int index, int width, IBigNumber** value)
{
	int i;
	int sel;
	sTerrisDevice* pobj;
	pobj = (sTerrisDevice*)objectThis(object);
	if (index != 8) /* 只响应8.ReadData端口 */
		return -1;
	if (pobj->wRead == 0)
		return -2; /* 上周期没有读命令,不响应,高阻状态 */
	if (pobj->bReadAddr == 0) {
		/* 偏移地址为0,读按键状态 */
		objectCall1(value, AssignInt32, pobj->keypressed);
		pobj->portdata[7] = pobj->keypressed;
		return 0;
	}
	return -2;
}

static int terrisdevice_hdl4se_unit_ClkTick(HOBJECT object)
{
	int i, j;
	int reset;
	sTerrisDevice* pobj;
	IBigNumber** temp = bigintegerCreate(32);
	pobj = (sTerrisDevice*)objectThis(object);
	pobj->keypressed = keypressed;

	for (i = 0; i < 9; i++) {
		if (0 == objectCall3(pobj->fromunit[i], GetValue, i, 32, temp)) {
			objectCall1(temp, GetInt32, &pobj->portdata[i]);
		}
	}
	objectRelease(temp);
	
	/* 读nwReset信号,看是否复位 */
	reset = pobj->portdata[0] & 1;
	if (reset == 0) {
		pobj->keypressed = 0;
	}
	

	/* 读写命令,看是否写 */
	if (reset != 0) {
		int wWrite = 0;
		unsigned int bWriteAddr = 0xFFFFFFFF;
		int wRead = 0;
		unsigned int bReadAddr = 0xFFFFFFFF;
		wWrite = pobj->portdata[2] & 1;
		bWriteAddr = pobj->portdata[3];
		if (wWrite && isMyAddr(bWriteAddr)) {
			bWriteAddr &= 0xff;
			unsigned int data = pobj->portdata[4];
			if (bWriteAddr >= 0x10 && bWriteAddr < 0xd0) {
				/*每个地址更新8个方块,每个方块4位,每一行
				不超过16个方块,这样两个字更新一行*/
				int yindex, xindex;
				yindex = (bWriteAddr - 0x10);
				yindex /= 4;
				if (yindex / 2 < YCOUNT) {
					int i;
					for (i = 0; i < 8; i++) {
						int xindex;
						xindex = i + (yindex & 1) * 8;
						if (xindex < XCOUNT) {
							pobj->panelvalue[yindex / 2][xindex] = data & 0xf;
							data >>= 4;
						}
					}
				}
			}
			else if (bWriteAddr == 0xe0 || bWriteAddr == 0xe4) {
				int offs;
				offs = (bWriteAddr == 0xe0) ? 0 : 2;
				pobj->nextblock[0 + offs][0] = data & 0xf; data >>= 4;
				pobj->nextblock[0 + offs][1] = data & 0xf; data >>= 4;
				pobj->nextblock[0 + offs][2] = data & 0xf; data >>= 4;
				pobj->nextblock[0 + offs][3] = data & 0xf; data >>= 4;
				pobj->nextblock[1 + offs][0] = data & 0xf; data >>= 4;
				pobj->nextblock[1 + offs][1] = data & 0xf; data >>= 4;
				pobj->nextblock[1 + offs][2] = data & 0xf; data >>= 4;
				pobj->nextblock[1 + offs][3] = data & 0xf; data >>= 4;
			}
			else if (bWriteAddr == 0xf0) {
				pobj->score = data;
			}
			else if (bWriteAddr == 0xf4) {
				pobj->level = data;
			}
			else if (bWriteAddr == 0xf8) {
				pobj->speed = data;
			}
		}
		wRead = pobj->portdata[6] & 0x1;
		bReadAddr = pobj->portdata[7];
		if (wRead && isMyAddr(bReadAddr)) {
			bReadAddr &= 0x1f;
			if (bReadAddr == 0x0) {
				pobj->wRead_cur = 1;
				pobj->bReadAddr_cur = 0;
			}
		}
	}

	return 0;
}

static const float blockcolor[16][3] = {
	{0.0f, 0.0f, 0.0f},
	{0.1f, 0.2f, 0.1f},
	{0.0f, 0.0f, 1.0f},
	{0.0f, 1.0f, 0.0f},
	{0.0f, 1.0f, 1.0f},
	{1.0f, 0.0f, 0.0f},
	{1.0f, 0.0f, 1.0f},
	{1.0f, 1.0f, 0.0f},
	{1.0f, 1.0f, 1.0f},
	{0.0f, 0.0f, 0.7f},
	{0.0f, 0.7f, 0.0f},
	{0.0f, 0.7f, 0.7f},
	{0.7f, 0.0f, 0.0f},
	{0.7f, 0.0f, 0.7f},
	{0.7f, 0.7f, 0.0f},
	{0.7f, 0.7f, 0.7f},
};

#define xofblock(ii) (-1.0f + (2.0f / (XCOUNT + 4)) * ii)
#define yofblock(jj) (-1.0f + (2.0f / YCOUNT) * jj)
#define wofblock() (2.0f / (XCOUNT + 4))
#define hofblock() (2.0f / YCOUNT)

static int terrisdevice_Draw_Block(int x, int y, int c)
{
	glColor3fv(blockcolor[c]);
	glBegin(GL_TRIANGLE_FAN);
	glVertex2f(xofblock(x), yofblock(y));
	glVertex2f(xofblock(x), yofblock(y) + hofblock());
	glVertex2f(xofblock(x) + wofblock(), yofblock(y) + hofblock());
	glVertex2f(xofblock(x) + wofblock(), yofblock(y));
	glEnd();
	glLineWidth(2.0f);
	glColor3f(0.0f, 0.10f, 0.25f);
	glBegin(GL_LINE_STRIP);
	glVertex2f(xofblock(x), yofblock(y));
	glVertex2f(xofblock(x), yofblock(y) + hofblock());
	glVertex2f(xofblock(x) + wofblock(), yofblock(y) + hofblock());
	glVertex2f(xofblock(x) + wofblock(), yofblock(y));
	glVertex2f(xofblock(x), yofblock(y));
	glEnd();

}

#include "digitdisp.h"

static int terrisdevice_Render(sTerrisDevice* pobj)
{
	int i, j;
	glfwGetFramebufferSize(pobj->window, &pobj->width, &pobj->height);
	glViewport(0, 0, pobj->width, pobj->height);
	glClearColor(0.0f, 0.10f, 0.25f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	/* pannel */
	for (i = 0; i < XCOUNT; i++) {
		for (j = 0; j < YCOUNT; j++) {
			terrisdevice_Draw_Block(i, j, pobj->panelvalue[j][i] & 0xf);
		}
	}

	/* current block */
	for (i = 0; i < 4; i++) {
		for (j = 0; j < 4; j++) {
			terrisdevice_Draw_Block(i + XCOUNT, 20 - j, pobj->nextblock[j][i] & 0xf);
		}
	}

#define LEFTPOS  0.57f
	/* levels */
	glColor3f(0.0f, 1.0f, 0.0f);
	drawsym(SYM_L, LEFTPOS, 0.30f, 0.01f, 0.015f);
	drawnum(pobj->level, 7, LEFTPOS + 0.04f, 0.30f, 0.01f, 0.015f);
	drawsym(SYM_P, LEFTPOS, 0.20f, 0.01f, 0.015f);
	drawnum(pobj->speed, 5, LEFTPOS + 0.04f, 0.20f, 0.01f, 0.015f);
	drawsym(SYM_C, LEFTPOS, 0.10f, 0.01f, 0.015f);
	drawnum(pobj->score, 8, LEFTPOS + 0.04f, 0.10f, 0.01f, 0.015f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	return 1;
}

int StopRunning();

static int terrisdevice_hdl4se_unit_Setup(HOBJECT object)
{
	sTerrisDevice* pobj;
	pobj = (sTerrisDevice*)objectThis(object);
	/*读信号和读地址寄存一拍*/
	pobj->wRead = pobj->wRead_cur;
	pobj->bReadAddr = pobj->bReadAddr_cur;
	pobj->count++;
	/* 每256个周期新绘制一次 */
	if ((pobj->count & 0xff) == 0) {
		if (terrisdevice_Render(pobj)) {
			/* Swap buffers */
			glfwSwapBuffers(pobj->window);
		}
	}

	glfwPollEvents();

	/* Check if we are still running */
	if (glfwWindowShouldClose(pobj->window))
		StopRunning();
	return 0;
}

static int terrisdevice_hdl4se_detector_GetName(HOBJECT object, const char** pname)
{
	sTerrisDevice* pobj;
	pobj = (sTerrisDevice*)objectThis(object);
	*pname = pobj->name;
	return 0;
}

static int terrisdevice_hdl4se_detector_GetSignalCount(HOBJECT object)
{
	sTerrisDevice* pobj;
	pobj = (sTerrisDevice*)objectThis(object);
	return 1;
}

static const char* terrisdevice_dataname[10] = {
	"wClk",
	"nwReset",
	"wWrite",
	"bWriteAddr",
	"bWriteData",
	"bWriteMask",
	"wRead",
	"bReadAddr",
	"bReadData",
	"BASEADDR"
};

static const char* terrisdevice_datawidth[10] ={
	1, 1, 1, 32, 32, 4, 1, 32, 32, 32
};

static int terrisdevice_hdl4se_detector_GetSignalInfo(HOBJECT object, int index, const char** pname, int * width)
{
	sTerrisDevice* pobj;
	pobj = (sTerrisDevice*)objectThis(object);
	if (index < 0 || index > 9)
		return -1;
	*pname = terrisdevice_dataname[index];
	*width = terrisdevice_datawidth[index];
	return 0;
}

static int terrisdevice_hdl4se_detector_GetSignalValue(HOBJECT object, int index, IBigNumber** value)
{
	sTerrisDevice* pobj;
	pobj = (sTerrisDevice*)objectThis(object);
	if (index < 0 || index > 9)
		return -1;
	objectCall2(value, SetWidth, 32, 0);
	if (index < 8)
		objectCall1(value, AssignInt32, pobj->portdata[index]);
	if (index == 8)
		objectCall1(value, AssignInt32, pobj->baseaddr);
	objectCall2(value, SetWidth, terrisdevice_datawidth[index], 0);
	return 0;
}

static int terrisdevice_hdl4se_detector_GetUnitCount(HOBJECT object)
{
	sTerrisDevice* pobj;
	pobj = (sTerrisDevice*)objectThis(object);
	return 0;
}

static int terrisdevice_hdl4se_detector_GetUnit(HOBJECT object, int index, HOBJECT* unit)
{
	sTerrisDevice* pobj;
	pobj = (sTerrisDevice*)objectThis(object);
	return 0;
}

8.2.3 LCOM八股 – 合

好了,对象已经实现了,下面就实现外部能调用的对象生成接口,其实这部分如果不实现,就由LCOM库来给出一个标准的生成函数,但是有一个这样的接口,用起来要方便很多,程序员也不能因为规范捆住手脚。

IHDL4SEUnit** guiCreate(unsigned int baseaddr, const char * name)
{
	int ret;
	IHDL4SEUnit** gui;
	PARAMITEM param[2];
	IHDL4SEUnit** result = NULL;
	param[0].name = PARAMID_BASEADDR;
	param[0].i32value = baseaddr;
	param[1].name = PARAMID_HDL4SE_UNIT_NAME;
	param[1].pvalue = name;
	A_u_t_o_registor_terrisdevice();
	ret = objectCreateEx(CLSID_TERRISDEVICE, param, 2, IID_HDL4SEUNIT, (const void**)&gui);
	return gui;
}

8.3 控制器

俄罗斯方块游系的控制器就稍微复杂点,我们分两个部分,一个部分是游戏逻辑的实现部分,一个部分是LCOM八股。

8.3.1 俄罗斯方块游戏的逻辑

这部分也每什么好讲的,直接上代码就是了,我们直接用c语言交流:

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "time.h"

enum TERRIS_KEY {
	TK_LEFT,
	TK_DOWN,
	TK_RIGHT,
	TK_TURNLEFT,
	TK_TURNRIGHT
};

#define PANELWIDTH	XCOUNT
#define PANELHEIGHT YCOUNT
#define PANELGAP    0
#define BLOCKSIZE 4

#define BLOCKSHAPES (sizeof(blockshape) / (sizeof(unsigned long) * 16))


/*块的形状,可以增加或删除其中的块,
也可以设计新的块形状*/
const unsigned long blockshape[][4][4] = 
{
	{
		{0,0,0,0},
		{0,1,1,0},
		{0,1,1,0},
		{0,0,0,0},
	},
	{
		{0,0,0,0},
		{0,1,1,0},
		{0,0,1,0},
		{0,0,0,0},
	},
	{
		{0,0,0,0},
		{0,1,0,0},
		{0,0,1,0},
		{0,0,0,0},
	},
/* 难	*/
	{
		{0,1,0,0},
		{1,1,1,0},
		{0,1,0,0},
		{0,0,0,0},
	},
	
	{
		{0,1,0,0},
		{0,1,0,0},
		{0,1,0,0},
		{0,1,0,0},
	},
/*	难*/
	{
		{0,0,0,0},
		{1,0,0,1},
		{1,1,1,1},
		{0,0,0,0},
	},

/*	变态*/	
	{
		{1,0,1,0},
		{0,1,0,0},
		{1,0,1,0},
		{0,0,0,0},
	},

	{
		{0,1,0,0},
		{0,1,1,0},
		{0,0,1,0},
		{0,0,0,0},
	},
	{
		{0,0,1,0},
		{0,1,1,0},
		{0,1,0,0},
		{0,0,0,0},
	},
	{
		{0,0,0,0},
		{0,1,0,0},
		{1,1,1,0},
		{0,0,0,0},
	},
	{
		{0,0,0,0},
		{0,1,0,0},
		{0,0,0,0},
		{0,0,0,0},
	},
	{
		{0,1,1,0},
		{0,0,1,0},
		{0,0,1,0},
		{0,0,0,0},
	},
	{
		{0,1,1,0},
		{0,1,0,0},
		{0,1,0,0},
		{0,0,0,0},
	},
};


typedef struct tagTerrisBlock
{
	int posx, posy;
	unsigned long subblock[BLOCKSIZE][BLOCKSIZE];
}TerrisBlock;

#define MAXSPPED  6000
static int gameScore = 0;
static int currentspeed = MAXSPPED;
static int gameLevel = 0;
static int currenttick = 0;
static TerrisBlock	  currentblock, nextblock;
static unsigned long terrisPanel[PANELHEIGHT][PANELWIDTH];

static int terrisBlockCanSetTo(TerrisBlock * pBlock, int x, int y);
static int terrisGenNewBlock(TerrisBlock * pBlock);

static void terrisInit()
{
	int i, j;
	/*初始化并开始*/
	srand( (unsigned)time( NULL ) );
	memset((char *)terrisPanel, 0, sizeof(terrisPanel));
	currentspeed = MAXSPPED;
	currenttick = 0;
	gameScore = 200;
	gameLevel = 0;

	/*边沿*/
	for (i = 0;i<PANELGAP;i++)
	{
		for (j = 0;j<PANELHEIGHT;j++)
		{
			terrisPanel[j][i] = 1;
			terrisPanel[j][i + PANELWIDTH - PANELGAP] = 1;
		}
	}
	for (i = 0;i<PANELGAP;i++)
	{
		for (j = 0;j<PANELWIDTH;j++)
			terrisPanel[PANELHEIGHT - PANELGAP + i][j] = 1;
	}
	terrisGenNewBlock(&currentblock);
	terrisGenNewBlock(&nextblock);
}

static void terrisBlockRotate(TerrisBlock * pBlock, int dir)
{
	/*dir 0:顺时针,否则,逆时针*/
	int i, j;
	unsigned long paneltemp[BLOCKSIZE][BLOCKSIZE];
	for (i = 0;i<BLOCKSIZE;i++)
	for (j = 0;j<BLOCKSIZE;j++)
	{
		if (dir==0)
			paneltemp[i][j] = pBlock->subblock[BLOCKSIZE-1-j][i];
		else
			paneltemp[i][j] = pBlock->subblock[j][BLOCKSIZE-1-i];
	}
	memcpy(pBlock->subblock, paneltemp, sizeof(paneltemp));
}

static int terrisBlockCanSetTo(TerrisBlock * pBlock, int x, int y)
{
	int i;
	int j;
	for (i = 0;i<BLOCKSIZE;i++)
	for (j = 0;j<BLOCKSIZE;j++)
	{
		if (pBlock->subblock[i][j] != 0)
		{
			int xx, yy;
			xx = x - BLOCKSIZE / 2 + j;
			yy = y - BLOCKSIZE / 2 + i;
			if (yy < 0)
				continue;
			if (yy >= PANELHEIGHT)
				return 0;
			if (xx < 0)
				return 0;
			if (xx >= PANELWIDTH)
				return 0;
			if (terrisPanel[yy][xx] != 0)
				return 0;
		}
	}
	return 1;
}

static int terrisCheckLine()
{
	int i;
	int j;
	int totallines;
	totallines = 0;
	for (i = 0;i<PANELHEIGHT - PANELGAP;i++)
	{
		int removeit;
		removeit = 1;
		for (j = PANELGAP;j<PANELWIDTH-PANELGAP;j++)
		{
			if (terrisPanel[i][j] == 0)
			{
				removeit = 0;
				break;
			}
		}
		if (removeit)
		{
			int k;
			for (k = i;k>0;k--)
			{
				for (j = 0;j<PANELWIDTH;j++)
					terrisPanel[k][j] = terrisPanel[k-1][j];
			}
			for (j = PANELGAP;j<PANELWIDTH-PANELGAP;j++)
				terrisPanel[0][j] = 0;
			
			totallines ++;
		}
	}
	gameLevel += totallines;
	if (totallines == 1)
		gameScore += 10;
	if (totallines == 2)
		gameScore += 40;
	if (totallines == 3)
		gameScore += 160;
	if (totallines == 4)
		gameScore += 640;
	currentspeed = MAXSPPED - gameScore / 10;
	if (currentspeed < 1)
		currentspeed = 1;
	return 0;
}

static int terrisGenNewBlock(TerrisBlock * pBlock)
{
	int i;
	int j;
	int shape;
	memset((char *)pBlock, 0, sizeof(TerrisBlock));
	pBlock->posx = PANELWIDTH / 2;
	pBlock->posy = 0;
	shape = rand() % BLOCKSHAPES;
	for (i = 0;i<BLOCKSIZE;i++)
	for (j = 0;j<BLOCKSIZE;j++)
	{
		unsigned long color;
		if (blockshape[shape][i][j] == 0)
			color = 0;
		else
			color = (rand() % 14) + 2;
		pBlock->subblock[i][j] = color;
	}
	return terrisBlockCanSetTo(pBlock, pBlock->posx, pBlock->posy);
}

static void terrisDown()
{		
	if (terrisBlockCanSetTo(&currentblock, currentblock.posx, currentblock.posy+1))
	{
		currentblock.posy ++;
	}
	else
	{
		int i;
		int j;
		for (i = 0;i<BLOCKSIZE;i++)
		{
			for (j = 0;j<BLOCKSIZE;j++)
			{
				if (currentblock.subblock[i][j] != 0)
				{
					int xx, yy;
					xx = currentblock.posx - BLOCKSIZE / 2 + j;
					yy = currentblock.posy - BLOCKSIZE / 2 + i;
					if (yy < 0)
						continue;
					if (yy >= PANELHEIGHT)
						continue;
					if (xx < 0)
						continue;
					if (xx >= PANELWIDTH)
						continue;
					if (terrisPanel[yy][xx] != 0)
						continue;
					terrisPanel[yy][xx] = currentblock.subblock[i][j];
				}
			}
		}
		terrisCheckLine();
		memcpy(&currentblock, &nextblock, sizeof(currentblock));
		if (terrisGenNewBlock(&nextblock) == 0)
		{
			terrisInit();
		}
	}
}

static int terrisTick()
{
	currenttick++;
	if ((currenttick % currentspeed) == 0)
	{
		terrisDown();
		return 1;
	}
	return 0;
}

static void terrisPressKey(int key)
{
	switch (key)
	{
	case TK_LEFT:/*left*/
		if (terrisBlockCanSetTo(&currentblock, currentblock.posx - 1, currentblock.posy))
			currentblock.posx--;
		gameScore--;
		break;
	case TK_DOWN:/*down*/
		gameScore += 2;
		terrisDown();
		break;
	case TK_RIGHT:/*right*/
		if (terrisBlockCanSetTo(&currentblock, currentblock.posx + 1, currentblock.posy))
			currentblock.posx++;
		gameScore--;
		break;
	case TK_TURNLEFT:/*逆时针1*/
		terrisBlockRotate(&currentblock, 1);
		if (terrisBlockCanSetTo(&currentblock, currentblock.posx, currentblock.posy)==0)
			terrisBlockRotate(&currentblock, 0);
		gameScore--;
		break;
	case TK_TURNRIGHT:/*顺时针0*/
		terrisBlockRotate(&currentblock, 0);
		if (terrisBlockCanSetTo(&currentblock, currentblock.posx, currentblock.posy)==0)
			terrisBlockRotate(&currentblock, 1);
		gameScore--;
		break;
	}
	if (gameScore < 0)
		terrisInit();
}

8.3.2 控制器作为HDL4SE基本单元

做为基本单元,也同样是LCOM八股,这里就不细说了,贴一部分代码出来交流。其中的IHDL4SEDetector的功能实际上没有实现,以后有需要再说。

typedef struct _sTerrisCtrl {
	OBJECT_HEADER
	INTERFACE_DECLARE(IHDL4SEUnit)
	HDL4SEUNIT_VARDECLARE
	INTERFACE_DECLARE(IHDL4SEDetector)
	HDL4SEDETECTOR_VARDECLARE
	DLIST_VARDECLARE
	
	IHDL4SEModule** parent;
	char* name;

	IHDL4SEUnit** readdata_unit;
	int           readdata_index;
	IBigNumber**  readdata;
	IBigNumber**  lastreaddata;

	unsigned int writeaddr;
	unsigned int writedata;

	int currentline;

}sTerrisCtrl;

OBJECT_FUNCDECLARE(terrisctrl, CLSID_TERRISCTRL);
HDL4SEUNIT_FUNCDECLARE(terrisctrl, CLSID_TERRISCTRL, sTerrisCtrl);
HDL4SEDETECTOR_FUNCDECLARE(terrisctrl, CLSID_TERRISCTRL, sTerrisCtrl);
DLIST_FUNCIMPL(terrisctrl, CLSID_TERRISCTRL, sTerrisCtrl);

OBJECT_FUNCIMPL(terrisctrl, sTerrisCtrl, CLSID_TERRISCTRL);


QUERYINTERFACE_BEGIN(terrisctrl, CLSID_TERRISCTRL)
QUERYINTERFACE_ITEM(IID_HDL4SEUNIT, IHDL4SEUnit, sTerrisCtrl)
QUERYINTERFACE_ITEM(IID_HDL4SEDETECTOR, IHDL4SEDetector, sTerrisCtrl)
QUERYINTERFACE_ITEM(IID_DLIST, IDList, sTerrisCtrl)
QUERYINTERFACE_END

static const char* terrisctrlModuleInfo()
{
	return "1.0.0-20210610.1806 Terris Controller";
}

static int terrisctrlCreate(const PARAMITEM* pParams, int paramcount, HOBJECT* pObject)
{
	sTerrisCtrl* pobj;
	int i;
	pobj = (sTerrisCtrl*)malloc(sizeof(sTerrisCtrl));
	if (pobj == NULL)
		return -1;
	*pObject = 0;
	HDL4SEUNIT_VARINIT(pobj, CLSID_TERRISCTRL);
	INTERFACE_INIT(IHDL4SEUnit, pobj, terrisctrl, hdl4se_unit);
	INTERFACE_INIT(IHDL4SEDetector, pobj, terrisctrl, hdl4se_detector);
	DLIST_VARINIT(pobj, terrisctrl);
	
	pobj->name = NULL;
	pobj->parent = NULL;
	pobj->readdata_unit = NULL;
	pobj->readdata = bigintegerCreate(32);
	pobj->lastreaddata = bigintegerCreate(32);
	pobj->currentline = 0;
	for (i = 0; i < paramcount; i++) {
		if (pParams[i].name == PARAMID_HDL4SE_UNIT_NAME) {
			if (pobj->name != NULL)
				free(pobj->name);
			pobj->name = strdup(pParams[i].pvalue);
		} 
		else if (pParams[i].name == PARAMID_HDL4SE_UNIT_PARENT) {
			pobj->parent = (IHDL4SEModule **)pParams[i].pvalue;
		}
	}
	terrisInit();
	/* 返回生成的对象 */
	OBJECT_RETURN_GEN(terrisctrl, pobj, pObject, CLSID_TERRISCTRL);
	return EIID_OK;
}

static void terrisctrlDestroy(HOBJECT object)
{
	sTerrisCtrl* pobj;
	int i;
	pobj = (sTerrisCtrl*)objectThis(object);
	if (pobj->name != NULL)
		free(pobj->name);
	memset(pobj, 0, sizeof(sTerrisCtrl));
	free(pobj);
}

static int terrisctrlValid(HOBJECT object)
{
	sTerrisCtrl* pobj;
	pobj = (sTerrisCtrl*)objectThis(object);
	return 1;
}

static int terrisctrl_hdl4se_unit_Connect(HOBJECT object, int index, HOBJECT from, int fromindex) 
{
	sTerrisCtrl* pobj;
	IHDL4SEUnit** unit = NULL;
	pobj = (sTerrisCtrl*)objectThis(object);
	if (index == 4) {
		if (0 == objectQueryInterface(from, IID_HDL4SEUNIT, (void**)&unit)) {
			objectRelease(pobj->readdata_unit);
			pobj->readdata_unit = unit;
			pobj->readdata_index = fromindex;
		}
	}
	return 0;
}

static int terrisctrl_hdl4se_unit_ConnectPart(HOBJECT object, int index, int start, int width, HOBJECT from, int fromindex)
{
	sTerrisCtrl* pobj;
	IHDL4SEUnit** unit = NULL;
	pobj = (sTerrisCtrl*)objectThis(object);
	return 0;
}

static int terrisctrl_hdl4se_unit_GetValue(HOBJECT object, int index, int width, IBigNumber ** value)
{
	int i;
	sTerrisCtrl* pobj;
	pobj = (sTerrisCtrl*)objectThis(object);
	if (index == 2) {
		objectCall1(value, AssignUint32, pobj->writeaddr);
	}
	else if (index == 3) {
		objectCall1(value, AssignUint32, pobj->writedata);
	}
	return 0;
}

static int terrisctrl_hdl4se_unit_ClkTick(HOBJECT object)
{
	sTerrisCtrl* pobj;

	pobj = (sTerrisCtrl*)objectThis(object);
	objectCall3(pobj->readdata_unit, GetValue, pobj->readdata_index, 32, pobj->readdata);
	if (!objectCall1(pobj->readdata, IsEQ, pobj->lastreaddata)) {
		unsigned int key;
		objectCall1(pobj->readdata, GetUint32, &key);
		objectCall1(pobj->lastreaddata, AssignU, pobj->readdata);
		if (key & 1)
			terrisPressKey(TK_RIGHT);
		if (key & 2)
			terrisPressKey(TK_LEFT);
		if (key & 4)
			terrisPressKey(TK_DOWN);
		if (key & 8)
			terrisPressKey(TK_TURNLEFT);
	}
	terrisTick();
	return 0;
}

static unsigned int get_line_data(sTerrisCtrl* pobj, int line, int right)
{
	unsigned int data;
	int i;
	data = 0;
	line = YCOUNT - 1 - line;
	for (i = 0; i < 8; i++) {
		int xx, yy, ii, jj;
		unsigned int c;
		xx = 7 - i + right * 8;
		yy = line;
		c = 0;
		jj = xx - currentblock.posx + BLOCKSIZE / 2;
		ii = yy - currentblock.posy + BLOCKSIZE / 2;
		if (ii >= 0 && ii < BLOCKSIZE && jj >= 0 && jj < BLOCKSIZE) {
			c = currentblock.subblock[ii][jj];
		}

		data <<= 4;
		if (c == 0)
			data |= terrisPanel[line][7 - i + right * 8] & 0xf;
		else
			data |= c & 0xf;

	}
	return data;
}

static int terrisctrl_hdl4se_unit_Setup(HOBJECT object)
{
	sTerrisCtrl* pobj;
	pobj = (sTerrisCtrl*)objectThis(object);
	/* 每个周期更新半行 */
	if (pobj->currentline >= 0 && pobj->currentline < YCOUNT * 2) {
		pobj->writeaddr = 0xF0000010 + pobj->currentline * 4;
		pobj->writedata = get_line_data(pobj, pobj->currentline / 2, pobj->currentline & 1);
	}
	else if (pobj->currentline == YCOUNT * 2 + 0) {
		pobj->writeaddr = 0xF00000e0;
		pobj->writedata = 
			  ((nextblock.subblock[0][0] & 0xf) << 0)
			| ((nextblock.subblock[0][1] & 0xf) << 4)
			| ((nextblock.subblock[0][2] & 0xf) << 8)
			| ((nextblock.subblock[0][3] & 0xf) << 12)
			| ((nextblock.subblock[1][0] & 0xf) << 16)
			| ((nextblock.subblock[1][1] & 0xf) << 20)
			| ((nextblock.subblock[1][2] & 0xf) << 24)
			| ((nextblock.subblock[1][3] & 0xf) << 28)
			;
	}
	else if (pobj->currentline == YCOUNT * 2 + 1) {
		pobj->writeaddr = 0xF00000e4;
		pobj->writedata =
			  ((nextblock.subblock[2][0] & 0xf) << 0)
			| ((nextblock.subblock[2][1] & 0xf) << 4)
			| ((nextblock.subblock[2][2] & 0xf) << 8)
			| ((nextblock.subblock[2][3] & 0xf) << 12)
			| ((nextblock.subblock[3][0] & 0xf) << 16)
			| ((nextblock.subblock[3][1] & 0xf) << 20)
			| ((nextblock.subblock[3][2] & 0xf) << 24)
			| ((nextblock.subblock[3][3] & 0xf) << 28)
			;
	}
	else if (pobj->currentline == YCOUNT * 2 + 2) {
		pobj->writeaddr = 0xF00000f0;
		pobj->writedata = gameScore;
	}
	else if (pobj->currentline == YCOUNT * 2 + 3) {
		pobj->writeaddr = 0xF00000f4;
		pobj->writedata = gameLevel;
	}
	else if (pobj->currentline == YCOUNT * 2 + 4) {
		pobj->writeaddr = 0xF00000f8;
		pobj->writedata = MAXSPPED - currentspeed;
	}
	pobj->currentline = pobj->currentline + 1;
	if (pobj->currentline > YCOUNT * 2 + 4)
		pobj->currentline = 0;
	return 0;
}

8.4 模拟器连接及运行

然后我们写verilog代码,把控制器连接到一个主模块:

/* main.v */

`include "hdl4secell.v"

/* 用c写的俄罗斯方块控制器 */
(* 
  HDL4SE="LCOM", 
  CLSID="6f8a9aa6-ec57-4734-a183-7871ed57ea95", 
  softmodule="hdl4se" 
*) 
module teris_ctrl
  (
    input  wClk,
    input  nwReset,
    output [31:0] bWriteAddr,
    output [31:0] bWriteData,
    input  [31:0] bKeyPressed
  );
endmodule

module main(
    input wClk, nwReset,
    output wWrite,
    output [31:0] bWriteAddr,
    output [31:0] bWriteData,
    output [3:0]  bWriteMask,
    output wRead,
    output [31:0] bReadAddr,
    input [31:0]  bReadData);

/* 游戏控制器 */
	teris_ctrl ctrl(wClk, nwReset, bWriteAddr, bWriteData, bReadData);

/*我们一直在读按键的状态*/
	hdl4se_const #(1, 1) const_wRead(wRead);
	hdl4se_const #(32, 32'hF000_0000) const_bReadAddr(bReadAddr);

/*总在写*/
    hdl4se_const #(1, 1) const_wWrite(wWrite);

endmodule

可以看到,控制作为一个基本单元挂到顶层模块中了,顶层模块的代码比较简单。那个编译器还没有做完,所以还是调用人形编译器把它编译成目标代码:

#include "stdlib.h"
#include "stdio.h"
#include "string.h"

#include "object.h"
#include "dlist.h"
#include "bignumber.h"
#include "hdl4secell.h"
#include "conststring.h"
#include "verilog_parsetree.h"

static const char* CLSID_000F = "6f8a9aa6-ec57-4734-a183-7871ed57ea95";

IHDL4SEUnit** hdl4seCreate_main2(IHDL4SEModule** parent, const char* instanceparam, const char* name)
{ /* module main */
	IHDL4SEModule** module;
	IHDL4SEUnit** unit;
	
	/* 生成模块对象 */
	unit = hdl4seCreateUnit(parent, CLSID_HDL4SE_MODULE, instanceparam, name);
	/* 得到对象的IHDL4SEModule 接口 */
	objectQueryInterface(unit, IID_HDL4SEMODULE, (void**)&module);

	/* 增加端口 */
	/*  0*/ objectCall3(module, AddPort, 1, PORT_DIRECT_INPUT, "wClk");
	/*  1*/ objectCall3(module, AddPort, 1, PORT_DIRECT_INPUT, "nwReset");
	/*  2*/ objectCall3(module, AddPort, 1, PORT_DIRECT_OUTPUT, "wWrite");
	/*  3*/ objectCall3(module, AddPort, 32, PORT_DIRECT_OUTPUT, "bWriteAddr");
	/*  4*/ objectCall3(module, AddPort, 32, PORT_DIRECT_OUTPUT, "bWriteData");
	/*  5*/ objectCall3(module, AddPort, 4, PORT_DIRECT_OUTPUT, "bWriteMask");
	/*  6*/ objectCall3(module, AddPort, 1, PORT_DIRECT_OUTPUT, "wRead");
	/*  7*/ objectCall3(module, AddPort, 32, PORT_DIRECT_OUTPUT, "bReadAddr");
	/*  8*/ objectCall3(module, AddPort, 32, PORT_DIRECT_INPUT, "bReadData");

	unsigned int clsid[4];
	str2iid(CLSID_000F, clsid);
	IHDL4SEUnit** ctrl = hdl4seCreateUnit(module, clsid, "", "ctrl");

	IHDL4SEUnit** const_1b1 = hdl4seCreateUnit(module, CLSID_HDL4SE_CONST, "1,1", "const_bReadAddr");
	objectCall3(unit, Connect, 2, const_1b1, 1); /* 简化处理,一直写 */
	objectCall3(unit, Connect, 3, ctrl, 2);
	objectCall3(unit, Connect, 4, ctrl, 3);
	IHDL4SEUnit** const_0_wRead = hdl4seCreateUnit(module, CLSID_HDL4SE_CONST, "1, 1", "const_0_wRead");
	objectCall3(unit, Connect, 6, const_0_wRead, 0);
	IHDL4SEUnit** const_bReadAddr = hdl4seCreateUnit(module, CLSID_HDL4SE_CONST, "32, 32'hF000_0000", "const_bReadAddr");
	objectCall3(unit, Connect, 7, const_bReadAddr, 0);
	objectCall3(ctrl, Connect, 4, unit, 8);

	/*释放module接口*/
	objectRelease(module);
	/*返回unit接口*/
	return unit;
}

然后将它连接到模拟器中:

#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "object.h"
#include "bignumber.h"
#include "hdl4secell.h"
#include "hdl4sesim.h"
#include "hdl4sevcdfile.h"
#include "terris.h"

IHDL4SESimulator** sim;
IHDL4SEUnit** topmodule; 
IHDL4SEUnit** gui;
unsigned long long clocks = 0;

static int running = 1;



int StopRunning()
{
	running = 0;
	return 0;
}

IHDL4SEUnit** hdl4seCreate_main(IHDL4SEModule** parent, const char* instanceparam, const char* name);
IHDL4SEUnit** hdl4seCreate_main2(IHDL4SEModule** parent, const char* instanceparam, const char* name);
extern int (*A_u_t_o_registor_terrisctrl)();

int main(int argc, char* argv[])
{
	int i;
	int width;
	int count, unitcount;
	IHDL4SEUnit** sim_unit;
	A_u_t_o_registor_terrisctrl();
	sim = hdl4sesimCreateSimulator();
	topmodule = hdl4seCreate_main2(NULL, "", "main");
	gui = guiCreate(0xf0000000, "terris");
	objectCall1(sim, SetTopModule, topmodule);
	objectCall1(sim, AddDevice, gui);
	objectCall1(sim, SetReset, 0);
	unitcount = 0;
	objectQueryInterface(sim, IID_HDL4SEUNIT, (void**) &sim_unit);
	objectPrintInfo(stdout, fprintf);
	do {
		//objectCall0(sim, RunClockTick);
		objectCall0(sim_unit, ClkTick);
		objectCall0(sim_unit, Setup);
		clocks++;
		if (clocks == 4)
			objectCall1(sim, SetReset, 1);
	} while (running);
	return 0;
}

然后,就可以打游戏了,呵呵呵呵呵…。这游戏太难了,自己改改代码,要么加个后门,一键清一半?

8.5 进展报告

还在推进编译器的软件编制,这部分还是比较复杂的。
上述代码用编译器生成后的目标代码如下:

/*
*  Created by HDL4SE @ Fri Jun 11 21:59:23 2021
*  Don't edit it.
*/

#include "stdlib.h"
#include "stdio.h"
#include "string.h"

#include "object.h"
#include "dlist.h"
#include "bignumber.h"
#include "hdl4secell.h"
#include "conststring.h"
#include "verilog_parsetree.h"


typedef IHDL4SEUnit** (*hdl4seCreate_Func)(IHDL4SEModule** parent, const char* instanceparam, const char* name); 

typedef struct s_module_info {
	int isbasiccell;
	int moduleindex;
	union {
		const char** clsid; 
		hdl4seCreate_Func *hdl4seCreate_func; 
	};
}module_info;

static const char *CLSID_000F = "6f8a9aa6-ec57-4734-a183-7871ed57ea95";
static const char *softmodule_000F = "hdl4se";
static const char *CLSID_000B = "8FBE5B87-B484-4f95-8291-DBEF86A1C354";
static const char *softmodule_000B = "hdl4se";

IHDL4SEUnit** hdl4seCreate_main(IHDL4SEModule** parent, const char* instanceparam, const char* name)
{ /* module main */
	IHDL4SEModule**	module;
	IHDL4SEUnit**	unit;
	IHDL4SEUnit**	modules[4];

	/* 生成模块对象 */
	unit = hdl4seCreateUnit(parent, CLSID_HDL4SE_MODULE, instanceparam, name);
	/* 得到对象的IHDL4SEModule 接口 */
	objectQueryInterface(unit, IID_HDL4SEMODULE, (void**)&module);

	/* 增加端口 */
	/*  0*/ objectCall3(module, AddPort, PORT_DIRECT_INPUT , 1, "wClk");
	/*  1*/ objectCall3(module, AddPort, PORT_DIRECT_INPUT , 1, "nwReset");
	/*  2*/ objectCall3(module, AddPort, PORT_DIRECT_OUTPUT, 1, "wWrite");
	{
		int portwidth;
		const expr_code expr_info[] = {
			{.exprtype=EXPRTYPE_NUMBER, .value="31" }, 
			{.exprtype=EXPRTYPE_NUMBER, .value="0" }, 
			{.exprtype=EXPRTYPE_BINOP, .op=OP_MINUS},
			{.exprtype=EXPRTYPE_NUMBER, .value="1"},
			{.exprtype=EXPRTYPE_BINOP, .op=OP_PLUS},
		};
		portwidth = const_expr_eval(5, expr_info, 0, NULL);
	/*  3*/ objectCall3(module, AddPort, PORT_DIRECT_OUTPUT, portwidth, "bWriteAddr");
	}
	{
		int portwidth;
		const expr_code expr_info[] = {
			{.exprtype=EXPRTYPE_NUMBER, .value="31" }, 
			{.exprtype=EXPRTYPE_NUMBER, .value="0" }, 
			{.exprtype=EXPRTYPE_BINOP, .op=OP_MINUS},
			{.exprtype=EXPRTYPE_NUMBER, .value="1"},
			{.exprtype=EXPRTYPE_BINOP, .op=OP_PLUS},
		};
		portwidth = const_expr_eval(5, expr_info, 0, NULL);
	/*  4*/ objectCall3(module, AddPort, PORT_DIRECT_OUTPUT, portwidth, "bWriteData");
	}
	{
		int portwidth;
		const expr_code expr_info[] = {
			{.exprtype=EXPRTYPE_NUMBER, .value="3" }, 
			{.exprtype=EXPRTYPE_NUMBER, .value="0" }, 
			{.exprtype=EXPRTYPE_BINOP, .op=OP_MINUS},
			{.exprtype=EXPRTYPE_NUMBER, .value="1"},
			{.exprtype=EXPRTYPE_BINOP, .op=OP_PLUS},
		};
		portwidth = const_expr_eval(5, expr_info, 0, NULL);
	/*  5*/ objectCall3(module, AddPort, PORT_DIRECT_OUTPUT, portwidth, "bWriteMask");
	}
	/*  6*/ objectCall3(module, AddPort, PORT_DIRECT_OUTPUT, 1, "wRead");
	{
		int portwidth;
		const expr_code expr_info[] = {
			{.exprtype=EXPRTYPE_NUMBER, .value="31" }, 
			{.exprtype=EXPRTYPE_NUMBER, .value="0" }, 
			{.exprtype=EXPRTYPE_BINOP, .op=OP_MINUS},
			{.exprtype=EXPRTYPE_NUMBER, .value="1"},
			{.exprtype=EXPRTYPE_BINOP, .op=OP_PLUS},
		};
		portwidth = const_expr_eval(5, expr_info, 0, NULL);
	/*  7*/ objectCall3(module, AddPort, PORT_DIRECT_OUTPUT, portwidth, "bReadAddr");
	}
	{
		int portwidth;
		const expr_code expr_info[] = {
			{.exprtype=EXPRTYPE_NUMBER, .value="31" }, 
			{.exprtype=EXPRTYPE_NUMBER, .value="0" }, 
			{.exprtype=EXPRTYPE_BINOP, .op=OP_MINUS},
			{.exprtype=EXPRTYPE_NUMBER, .value="1"},
			{.exprtype=EXPRTYPE_BINOP, .op=OP_PLUS},
		};
		portwidth = const_expr_eval(5, expr_info, 0, NULL);
	/*  8*/ objectCall3(module, AddPort, PORT_DIRECT_INPUT , portwidth, "bReadData");
	}

	{/* 模块实例化 */
		int i;
		static const char *inst_name[4] = {
			/*  0*/ "ctrl",
			/*  1*/ "const_wRead",
			/*  2*/ "const_bReadAddr",
			/*  3*/ "const_wWrite",
		};

		static module_info inst_module_info[4] = {
			/*  0*/ {1, 0x000F, &CLSID_000F},
			/*  1*/ {1, 0x000B, &CLSID_000B},
			/*  2*/ {1, 0x000B, &CLSID_000B},
			/*  3*/ {1, 0x000B, &CLSID_000B},
		};
		for(i = 0; i< 4; i++) { 
			char instanceparam[256];
			strcpy(instanceparam, "");
			if (inst_module_info[i].isbasiccell) {
				unsigned int clsid[4];
				str2iid(*inst_module_info[i].clsid, clsid);
				modules[i] = hdl4seCreateUnit(module, clsid, instanceparam, inst_name[i]);
			} else {
				modules[i] = (*inst_module_info[i].hdl4seCreate_func)(module, instanceparam, inst_name[i]);
			}
		}
	}

	/* 线网连接 */

	/* instance "ctrl", module "teris_ctrl" */
		objectCall3(modules[0], Connect, 0, unit, 0);
		objectCall3(modules[0], Connect, 1, unit, 1);
		objectCall3(unit, Connect, 3, modules[0], 2);
		objectCall3(unit, Connect, 4, modules[0], 3);
		objectCall3(modules[0], Connect, 4, unit, 8);

	/* instance "const_wRead", module "hdl4se_const" */
		objectCall3(unit, Connect, 6, modules[1], 0);

	/* instance "const_bReadAddr", module "hdl4se_const" */
		objectCall3(unit, Connect, 7, modules[2], 0);

	/* instance "const_wWrite", module "hdl4se_const" */
		objectCall3(unit, Connect, 2, modules[3], 0);

	/*释放module接口*/
	objectRelease(module);
	/*返回unit接口*/
	return unit;
}

就差那么一点点了,打游戏误了进度,但就是忍不住玩一把,特别是自己编的那种,改改代码玩一局,再改改再玩一局,时间就这么过去了。
【请参考】
1.HDL4SE:软件工程师学习Verilog语言(七)
2.HDL4SE:软件工程师学习Verilog语言(六)
3.HDL4SE:软件工程师学习Verilog语言(五)
4.HDL4SE:软件工程师学习Verilog语言(四)
5.HDL4SE:软件工程师学习Verilog语言(三)
6.HDL4SE:软件工程师学习Verilog语言(二)
7.HDL4SE:软件工程师学习Verilog语言(一)
8.LCOM:轻量级组件对象模型
9.LCOM:带数据的接口
10.工具下载:在64位windows下的bison 3.7和flex 2.6.4
11.git: verilog-parser开源项目
12.git: HDL4SE项目
13.git: LCOM项目
14.git: GLFW项目

  • 1
    点赞
  • 2
    评论
  • 1
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

评论 2 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:游动-白 设计师:我叫白小胖 返回首页

打赏作者

饶先宏

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值