多个Quads共用一个外部时钟的方法

多个Quads共用外部时钟的方法

在使用Xilinx的高速收发器时,可能会遇到多个Quads共用一个外部时钟的情况。原因可能是节省资源或者减小PCB面积等等吧,那么实际中多个Quads共用一个外部时钟?答案是“肯定的”。

从Xilinx手册UG476中的说明可以知道,一个外部参考时钟,可以为其所在Quad的上方Quad提供时钟,也可以为其所在Quad的下方Quad提供时钟。一个外部时钟源能给包括自身在内的三个Quad提供时钟。如下图所示:

我尝试以使用一个外部时钟实现12路SRIO1X通信为例来讲解这个问题。首先,这个外部时钟必须接到中间的Quad的时钟引脚上。如外部时钟接到Quad116上,那么就可以使用Quad115、Quad116和Quad117来实现12路SRIO1X。开始工程搭建,常规做法是使用Xilinx自带的example工程,然后在此工程上进行修改。

但直接例化12个Example工程中的模块是不行的。因为Example工程的时钟模块例化了IBUFDS_GTE3的原语,但实际中时钟输入只有一个,这种情况下,综合是不会成功的。如下图所示:

解决方法也很简单,将srio_gen2_0_srio_clk.v的IBUFDS_GTE3原语例化注释掉。将IBUFDS_GTE3原语的例化放置到SRIO1X例化的外层,只例化一个IBUFDS_GTE3,这个IBUFDS_GTE3的refclk输出为所有例化的SRIO1X提供参考时钟。

<think> 我们使用freeglut来渲染一个魔方。魔方由27个小立方体组成(3x3x3)。每个小立方体有6个面,但我们只渲染可见的面(即魔方外表面)。为了简化,我们给每个面不同的颜色,这样更容易区分。 步骤: 1. 初始化freeglut,设置显示模式、窗口大小和标题。 2. 设置投影矩阵(透视投影)和模型视图矩阵。 3. 定义绘制立方体的函数:绘制一个边长为2的立方体,中心在原点。然后通过平移将27个小立方体绘制在正确的位置。 4. 在绘制函数中,我们遍历3个维度(x, y, z),每个维度从-1,0,1(即三个偏移量)来定位每个小立方体的中心位置。 5. 给每个小立方体的各个面设置颜色。注意:魔方的每个面(前、后、左、右、上、下)应该有统一的颜色,但为了区分,我们给每个面不同的颜色。在实际魔方中,外表面的颜色是固定的,内部的面不可见,所以我们不需要绘制内部的面。但为了简化,我们绘制所有小立方体,但注意在魔方内部的面会被外部遮挡,所以即使绘制了也看不到。 但是,为了效率,我们可以只绘制外表面。然而,本示例为了简单,绘制所有小立方体,并让深度测试来处理遮挡。 6. 设置颜色时,我们可以为每个面指定不同的颜色。这里我们按照标准魔方的颜色设置: - 前面:绿色 - 后面:蓝色 - 左面:橙色 - 右面:红色 - 上面:白色 - 下面:黄色 但是注意,每个小立方体在魔方中的位置决定了哪些面是可见的(即外表面)。比如,角上的立方体有3个外表面,棱上的有2个,中心的有1个,最中心的是完全不可见的(但为了简单,我们全部绘制,因为内部会被外部遮挡)。 7. 我们为每个小立方体的每个面设置颜色。但是,同一个大面上的中心小立方体的那个面应该是同一个颜色。这里我们简化处理,给每个小立方体的每个面都按照其在大魔方中的位置赋予颜色。例如,前面的小立方体的前面都设为绿色,后面的小立方体的后面都设为蓝色,等等。 然而,这样需要判断每个小立方体在魔方中的位置,然后决定每个面的颜色。由于我们只有3x3x3,我们可以分别判断每个维度的位置(-1,0,1),然后根据位置决定哪些面是外表面,并赋予相应的颜色。 但是,为了简化代码,我们直接绘制27个立方体,每个立方体的六个面都按照其所在位置赋予颜色。例如,对于x=-1(左面)的小立方体,其左面应该是橙色;x=1(右面)的小立方体,其右面是红色;同理y=-1(下面)的底面是黄色,y=1(上面)的顶面是白色;z=1(前面)的前面是绿色,z=-1(后面)的后面是蓝色。 注意:我们绘制的立方体中心在(0,0,0),边长为2,所以每个小立方体从中心到面是1个单位。因此,整个魔方从x=-3到3,y=-3到3,z=-3到3?不对,应该是每个小立方体边长2,相邻立方体中心距离2,所以整个魔方边长6,从-3到3。 但是,我们实际偏移量是:每个维度上,位置为-2,0,2(因为每个小立方体中心相距2个单位)。所以整个魔方从x=-3到3(即-2-1到2+1)?不对,立方体从中心到边的距离是1,所以一个立方体占据[-1,1]区间。那么27个立方体,以3x3x3排列,每个立方体中心在(-2,0,2)等位置。整个魔方占据x轴从-2-1到2+1即-3到3,其他轴同理。 具体位置:三个偏移量:-2, 0, 2(三个值,每个维度上三个位置)。 8. 在绘制每个小立方体时,我们根据当前小立方体的位置(x_index, y_index, z_index)来决定每个面的颜色。例如: - 如果当前小立方体的z_index=2(即最前面的那一层),那么它的前面(+Z面)应该是绿色。 - 如果z_index=-2(最后面),那么它的后面(-Z面)应该是蓝色。 - 同理,x_index=-2(最左边)的左边是橙色,x_index=2(最右边)的右边是红色。 - y_index=2(最上面)的上面是白色,y_index=-2(最下面)的下面是黄色。 对于中间的小立方体,它的某些面不是外表面,但是我们仍然给它颜色(比如中间立方体的前面,在魔方内部,不会被看到,但我们还是赋予颜色,不影响)。 9. 我们使用glTranslatef来移动坐标系,然后绘制立方体。 10. 开启深度测试,这样立方体之间可以正确遮挡。 11. 添加旋转控制,以便用鼠标旋转整个魔方。 代码结构: 初始化glut 注册显示回调函数:在显示回调函数中,清除颜色和深度缓冲,设置模型视图矩阵,绘制27个立方体。 注册 reshape 回调:设置视口和投影矩阵(透视投影)。 注册键盘回调,用于退出等。 设置旋转:我们可以用glutMotionFunc和glutMouseFunc来实现鼠标拖动旋转。 由于我们使用freeglut,可以使用glutSolidCube来绘制立方体,但glutSolidCube绘制的是中心在原点,边长为1的立方体。所以我们需要缩放(放大2倍)?或者我们可以使用glutSolidCube(2.0)来绘制边长为2的立方体。 但是,我们也可以自己绘制立方体(用glBegin(GL_QUADS)),这样我们可以为每个面单独设置颜色。 这里我们选择自己绘制立方体,因为需要为每个面设置不同的颜色。 下面我们用代码实现: 注意:坐标系统,我们使用右手坐标系,Z轴正向指向屏幕外,负向指向屏幕内。但OpenGL默认是右手坐标系(摄像机看向-Z方向)。所以我们绘制时,前面(朝向用户)是+Z方向,后面是-Z方向。 立方体顶点:从中心(0,0,0)出发,边长为2,那么每个面的中心到立方体中心距离为1,所以顶点坐标范围为:x:[-1,1], y:[-1,1], z:[-1,1]。但注意,我们绘制每个面时,需要指定4个顶点。 我们定义立方体的6个面,每个面4个顶点,并按照顺序(逆时针,这样正面是外面)来绘制。 但是,为了简化,我们可以写一个函数draw_cube(),然后在里面为每个面设置颜色之前,先根据当前小立方体的位置判断这个面是否应该显示为什么颜色(外表面颜色),否则就显示黑色(或者内部面我们不绘制?但是为了简单,我们全部绘制,内部面用灰色)。 但是,我们这里按照标准魔方颜色来绘制外表面,内部面用灰色(或者黑色)?这样在旋转的时候,如果看到内部,就会看到灰色。但我们也可以只绘制外表面,不过这样需要判断每个小立方体的每个面是否在外部,代码会复杂一些。 为了教学目的,我们绘制全部的面,内部面用深灰色。 步骤: 在绘制一个小立方体之前,我们根据这个立方体的位置(偏移量)判断它在魔方中的位置(哪个层)。 然后,我们绘制立方体的六个面,对于每个面,判断这个面是不是魔方的外表面: 前面(+Z):如果这个立方体的z偏移量是2(即最前面一层),那么这个面是外表面,用绿色。 后面(-Z):如果z偏移量是-2,用蓝色。 左面(-X):如果x偏移量是-2,用橙色。 右面(+X):如果x偏移量是2,用红色。 上面(+Y):如果y偏移量是2,用白色。 下面(-Y):如果y偏移量是-2,用黄色。 如果都不是,那么这个面是内部面,我们用深灰色(0.2,0.2,0.2)。 但是,注意:每个小立方体最多只有三个外表面(角块),也可能有两个(棱块),一个(中心块),或者没有(最中心的小立方体,但3x3魔方没有最中心,因为3是奇数,中心块有一个外表面)。所以判断每个面。 我们写一个函数,给定当前立方体的位置(x, y, z)偏移量(只能是-2,0,2中的一个),然后给定要绘制的面的法线方向(用来判断是哪个面),然后决定颜色。 由于我们绘制每个面时,知道这个面的法线方向(即我们绘制的面是+X, -X, +Y, -Y, +Z, -Z),所以我们可以这样判断。 但是,在绘制函数中,我们分别绘制六个面,并为每个面设置颜色。 具体代码: 定义偏移量:三个轴上都是[-2,0,2] 然后三重循环。 注意:由于整个魔方中心在原点,所以每个小立方体的位置就是(offset_x, offset_y, offset_z) 在绘制每个小立方体时,我们glPushMatrix,然后glTranslatef(offset_x, offset_y, offset_z),然后在局部坐标系(此时位于小立方体中心)绘制边长为2的立方体。 绘制立方体的函数draw_single_cube(offset_x, offset_y, offset_z): 对于六个面,每个面: 判断这个面在当前小立方体的位置下是否是外表面,然后设置颜色。 然后绘制这个面(四个顶点)。 顶点坐标可以预先定义。 由于每个面是正方形,我们可以这样定义顶点(相对于立方体中心): 前面(+Z):四个点:(1,1,1), (-1,1,1), (-1,-1,1), (1,-1,1) -> 注意法线是(0,0,1) 后面(-Z):( -1,1,-1), (1,1,-1), (1,-1,-1), (-1,-1,-1) -> 法线(0,0,-1) 左面(-X):(-1,1,1), (-1,1,-1), (-1,-1,-1), (-1,-1,1) -> 法线(-1,0,0) 右面(+X):(1,1,-1), (1,1,1), (1,-1,1), (1,-1,-1) -> 法线(1,0,0) 上面(+Y):( -1,1,-1), (1,1,-1), (1,1,1), (-1,1,1) -> 法线(0,1,0) 下面(-Y):( -1,-1,1), (1,-1,1), (1,-1,-1), (-1,-1,-1) -> 法线(0,-1,0) 但是,注意顶点顺序(逆时针从外部看)。 我们开始写代码: 注意:使用freeglut需要链接相应的库。在Linux下编译可能需要:-lglut -lGL -lGLU 在Windows下需要链接freeglut库。 另外,我们添加旋转功能:用鼠标拖动旋转整个魔方。我们记录两个角度:绕y轴旋转和绕x轴旋转。在显示函数中,我们先绕y轴旋转,再绕x轴旋转,然后绘制魔方。 我们定义全局变量: float rot_x = 0.0f; float rot_y = 0.0f; int last_x = 0, last_y = 0; bool mouse_down = false; 在鼠标按下时,记录当前位置,并设置mouse_down=true。 在鼠标移动时,如果mouse_down,计算位移,然后更新rot_x和rot_y。 由于代码较长,我们逐步实现。 以下代码使用C++和freeglut。 </think># 使用FreeGLUT渲染魔方 下面我将实现一个使用FreeGLUT渲染的3x3魔方,支持旋转、视角控制和单面旋转功能。 ## 解决方案 ```cpp #include <GL/freeglut.h> #include <vector> #include <cmath> // 魔方常量 const int CUBE_SIZE = 3; // 3x3x3魔方 const float CUBIE_SPACING = 0.05f; // 小立方体之间的间距 const float CUBIE_SIZE = 0.3f; // 小立方体尺寸 // 旋转控制 float globalRotationX = 30.0f; float globalRotationY = -30.0f; int lastMouseX = 0, lastMouseY = 0; bool mouseLeftDown = false; // 面旋转控制 bool rotatingFace = false; int rotatingFaceAxis = 0; // 0=x, 1=y, 2=z int rotatingFaceIndex = 0; float faceRotationAngle = 0.0f; float targetRotationAngle = 0.0f; // 魔方颜色 GLfloat colors[6][3] = { {1.0f, 0.0f, 0.0f}, // 红 - 右面 {0.0f, 1.0f, 0.0f}, // 绿 - 前面 {0.0f, 0.0f, 1.0f}, // 蓝 - 后面 {1.0f, 1.0f, 0.0f}, // 黄 - 下面 {1.0f, 1.0f, 1.0f}, // 白 - 上面 {1.0f, 0.5f, 0.0f} // 橙 - 左面 }; // 绘制单个小立方体 void drawCubie(float x, float y, float z) { float halfSize = CUBIE_SIZE / 2.0f; glPushMatrix(); glTranslatef(x, y, z); // 绘制立方体的六个面 glBegin(GL_QUADS); // 右面 (红) glColor3fv(colors[0]); glVertex3f(halfSize, -halfSize, -halfSize); glVertex3f(halfSize, halfSize, -halfSize); glVertex3f(halfSize, halfSize, halfSize); glVertex3f(halfSize, -halfSize, halfSize); // 左面 (橙) glColor3fv(colors[5]); glVertex3f(-halfSize, -halfSize, halfSize); glVertex3f(-halfSize, halfSize, halfSize); glVertex3f(-halfSize, halfSize, -halfSize); glVertex3f(-halfSize, -halfSize, -halfSize); // 上面 (白) glColor3fv(colors[4]); glVertex3f(-halfSize, halfSize, halfSize); glVertex3f(halfSize, halfSize, halfSize); glVertex3f(halfSize, halfSize, -halfSize); glVertex3f(-halfSize, halfSize, -halfSize); // 下面 (黄) glColor3fv(colors[3]); glVertex3f(-halfSize, -halfSize, -halfSize); glVertex3f(halfSize, -halfSize, -halfSize); glVertex3f(halfSize, -halfSize, halfSize); glVertex3f(-halfSize, -halfSize, halfSize); // 前面 (绿) glColor3fv(colors[1]); glVertex3f(-halfSize, -halfSize, halfSize); glVertex3f(halfSize, -halfSize, halfSize); glVertex3f(halfSize, halfSize, halfSize); glVertex3f(-halfSize, halfSize, halfSize); // 后面 (蓝) glColor3fv(colors[2]); glVertex3f(halfSize, -halfSize, -halfSize); glVertex3f(-halfSize, -halfSize, -halfSize); glVertex3f(-halfSize, halfSize, -halfSize); glVertex3f(halfSize, halfSize, -halfSize); glEnd(); glPopMatrix(); } // 绘制魔方 void drawRubiksCube() { float startPos = -((CUBE_SIZE - 1) * (CUBIE_SIZE + CUBIE_SPACING)) / 2.0f; for (int x = 0; x < CUBE_SIZE; x++) { for (int y = 0; y < CUBE_SIZE; y++) { for (int z = 0; z < CUBE_SIZE; z++) { // 跳过中心立方体(不可见) if (x == 1 && y == 1 && z == 1) continue; // 计算位置 float posX = startPos + x * (CUBIE_SIZE + CUBIE_SPACING); float posY = startPos + y * (CUBIE_SIZE + CUBIE_SPACING); float posZ = startPos + z * (CUBIE_SIZE + CUBIE_SPACING); // 应用面旋转 if (rotatingFace) { if (rotatingFaceAxis == 0 && x == rotatingFaceIndex) { // X轴旋转 glPushMatrix(); glRotatef(faceRotationAngle, 1, 0, 0); } else if (rotatingFaceAxis == 1 && y == rotatingFaceIndex) { // Y轴旋转 glPushMatrix(); glRotatef(faceRotationAngle, 0, 1, 0); } else if (rotatingFaceAxis == 2 && z == rotatingFaceIndex) { // Z轴旋转 glPushMatrix(); glRotatef(faceRotationAngle, 0, 0, 1); } } drawCubie(posX, posY, posZ); if (rotatingFace) { if ((rotatingFaceAxis == 0 && x == rotatingFaceIndex) || (rotatingFaceAxis == 1 && y == rotatingFaceIndex) || (rotatingFaceAxis == 2 && z == rotatingFaceIndex)) { glPopMatrix(); } } } } } } // 显示回调函数 void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); // 设置相机 gluLookAt(0, 0, 5, // 相机位置 0, 0, 0, // 观察点 0, 1, 0); // 上方向 // 应用全局旋转 glRotatef(globalRotationX, 1, 0, 0); glRotatef(globalRotationY, 0, 1, 0); // 绘制魔方 drawRubiksCube(); glutSwapBuffers(); } // 更新面旋转 void updateFaceRotation() { if (rotatingFace) { float rotationStep = 5.0f; if (faceRotationAngle < targetRotationAngle) { faceRotationAngle += rotationStep; if (faceRotationAngle > targetRotationAngle) faceRotationAngle = targetRotationAngle; } else if (faceRotationAngle > targetRotationAngle) { faceRotationAngle -= rotationStep; if (faceRotationAngle < targetRotationAngle) faceRotationAngle = targetRotationAngle; } if (faceRotationAngle == targetRotationAngle) { rotatingFace = false; } } } // 空闲回调函数 void idle() { updateFaceRotation(); glutPostRedisplay(); } // 鼠标点击回调 void mouse(int button, int state, int x, int y) { if (button == GLUT_LEFT_BUTTON) { mouseLeftDown = (state == GLUT_DOWN); lastMouseX = x; lastMouseY = y; } } // 鼠标移动回调 void mouseMotion(int x, int y) { if (mouseLeftDown) { globalRotationY += (x - lastMouseX); globalRotationX += (y - lastMouseY); lastMouseX = x; lastMouseY = y; glutPostRedisplay(); } } // 键盘回调 void keyboard(unsigned char key, int x, int y) { switch (key) { // X轴旋转 case 'q': startFaceRotation(0, 0); break; // 左面 case 'w': startFaceRotation(0, 1); break; // 中间 case 'e': startFaceRotation(0, 2); break; // 右面 // Y轴旋转 case 'a': startFaceRotation(1, 0); break; // 下面 case 's': startFaceRotation(1, 1); break; // 中间 case 'd': startFaceRotation(1, 2); break; // 上面 // Z轴旋转 case 'z': startFaceRotation(2, 0); break; // 后面 case 'x': startFaceRotation(2, 1); break; // 中间 case 'c': startFaceRotation(2, 2); break; // 前面 case 27: // ESC键 exit(0); break; } } // 开始面旋转 void startFaceRotation(int axis, int index) { if (!rotatingFace) { rotatingFace = true; rotatingFaceAxis = axis; rotatingFaceIndex = index; targetRotationAngle = (faceRotationAngle >= 360.0f) ? 90.0f : faceRotationAngle + 90.0f; } } // 窗口大小变化回调 void reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 0.1f, 100.0f); glMatrixMode(GL_MODELVIEW); } // 初始化OpenGL void initGL() { glClearColor(0.2f, 0.2f, 0.2f, 1.0f); // 深灰色背景 glEnable(GL_DEPTH_TEST); // 启用深度测试 glEnable(GL_CULL_FACE); // 启用面剔除 glCullFace(GL_BACK); // 剔除背面 } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800, 600); glutCreateWindow("FreeGLUT魔方渲染"); initGL(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMouseFunc(mouse); glutMotionFunc(mouseMotion); glutIdleFunc(idle); glutMainLoop(); return 0; } ``` ## 代码解释 ### 1. 数据结构与常量 - `CUBE_SIZE`: 定义魔方大小为3x3x3 - `CUBIE_SPACING`: 小立方体之间的间距 - `CUBIE_SIZE`: 每个小立方体的大小 - `colors`: 定义魔方六个面的标准颜色(红、绿、蓝、黄、白、橙) ### 2. 旋转控制 - **全局旋转**:通过鼠标拖动实现魔方整体旋转 - **面旋转**:通过键盘控制特定面的旋转 - `rotatingFaceAxis`: 旋转轴(0=x, 1=y, 2=z) - `rotatingFaceIndex`: 旋转面的索引(0=左/下/后, 1=中, 2=右/上/前) ### 3. 核心函数 - `drawCubie()`: 绘制单个小立方体,设置六面颜色 - `drawRubiksCube()`: 绘制整个魔方,处理面旋转 - `startFaceRotation()`: 初始化面旋转参数 - `updateFaceRotation()`: 更新旋转动画状态 ### 4. OpenGL设置 - `initGL()`: 初始化OpenGL状态,启用深度测试和面剔除 - `reshape()`: 处理窗口大小变化,设置透视投影 - `display()`: 主渲染函数,设置相机和全局旋转 ### 5. 交互控制 - 鼠标左键拖动:旋转整个魔方 - 键盘控制: - Q/W/E:旋转X轴不同面(左/中/右) - A/S/D:旋转Y轴不同面(下/中/上) - Z/X/C:旋转Z轴不同面(后/中/前) - ESC:退出程序 ### 6. 渲染优化 - 跳过中心立方体渲染(不可见) - 使用面剔除提高渲染效率 - 使用双缓冲避免闪烁 - 平滑旋转动画 ## 编译与运行 ### 编译指令 (Linux) ```bash g++ -o rubiks_cube rubiks_cube.cpp -lglut -lGL -lGLU ``` ### 编译指令 (Windows) 使用Visual Studio配置FreeGLUT库并编译 ### 运行 ```bash ./rubiks_cube ``` ## 操作指南 1. 鼠标左键拖动:旋转整个魔方 2. 键盘Q/W/E:旋转魔方的左/中/右面(X轴) 3. 键盘A/S/D:旋转魔方的下/中/上面(Y轴) 4. 键盘Z/X/C:旋转魔方的后/中/前面(Z轴) 5. ESC键:退出程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值